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..2d49c75 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(); @@ -55,31 +57,19 @@ void TablesTableModel::refresh() if (!m_catalog) return; - // Later afscheiden naar filter functie auto classes = m_catalog->classes(); + 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()); + auto [rn, oidIndex] = treeBuilder.Build(); + rootNode = rn; - 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); - } - } StartLoadTableSizes(std::move(oidIndex)); } @@ -106,14 +96,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,37 +164,29 @@ 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; -} - -Oid TablesTableModel::getTableOid(int row) const -{ - return m_tables[row]._class.oid(); -} - QVariant TablesTableModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) @@ -195,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] @@ -205,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); }); } @@ -254,18 +268,41 @@ 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()) { - m_tables[findIter->second].sizes = s; + findIter->second->sizes = s; } } emit dataChanged( - createIndex(0, TotalSizeCol), - createIndex(static_cast(m_tables.size()), ToastSizeCol) - ); + index(0, TotalSizeCol), + index(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 54% rename from pglab/catalog/models/TablesTableModel.h rename to pglab/catalog/tables/TablesTableModel.h index 880dcd8..2e02aa5 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,31 @@ 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; + + RowItem rowItem(const QModelIndex &index) const { - return getTable(row); + return nodeFromIndex(index)->_class; } - 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; + std::shared_ptr rootNode; QMetaObject::Connection refreshConnection; std::shared_ptr openDatabase; @@ -83,10 +75,17 @@ private: 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); + using OidClassIndex = std::map>; + void StartLoadTableSizes(OidClassIndex oidIndex); + TableSizes QueryTableSizes() const; + void PopulateSizes( + const OidClassIndex &oidIndex, + const 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..d2d03f5 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" @@ -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); @@ -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/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/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 67b5e55..e8c2363 100644 --- a/pglab/util/PgLabTableViewHelper.h +++ b/pglab/util/PgLabTableViewHelper.h @@ -1,25 +1,51 @@ #pragma once #include +#include #include +#include "util/PgLabItemDelegate.h" #include "util/PgLabTableView.h" #include class PgDatabaseCatalog; -template +template +void ResizeColumnsToContent(ViewType *vt) +{ + vt->resizeColumnsToContents(); +} + +template <> +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 class PgLabTableViewHelper { public: PgLabTableViewHelper(QWidget * parent, TableModel *tableModel) { - m_tableView = new PgLabTableView(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); } @@ -27,9 +53,9 @@ public: : PgLabTableViewHelper(parent, new TableModel(parent)) {} - PgLabTableView *tableView() const + ViewType *itemView() const { - return m_tableView; + return m_itemView; } TableModel *dataModel() const @@ -45,27 +71,27 @@ public: void setCatalog(std::shared_ptr cat) { m_dataModel->setCatalog(cat); - m_tableView->resizeColumnsToContents(); + ResizeColumnsToContent(m_itemView); } QModelIndex currentSourceIndex() const { - QModelIndex index = m_tableView->selectionModel()->currentIndex(); + QModelIndex index = m_itemView->selectionModel()->currentIndex(); if (!index.isValid()) return index; 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 +105,12 @@ public: if (!index.isValid()) return {}; - return rowItemForSourceRow(index.row()); + return rowItemForSourceRow(index); } private: - PgLabTableView *m_tableView = nullptr; + ViewType *m_itemView = 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..44a3269 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp @@ -0,0 +1,82 @@ +#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::tuple, std::map>> 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); + return { rootNode, std::move(processedNodes) }; +} + + +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()) + addToToplevel(node); + else + addToParents(node, parents); + return node; +} + +void TableTreeBuilder::addToToplevel(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..99ccdb1 --- /dev/null +++ b/pglablib/ui/catalog/tables/TableTreeBuilder.h @@ -0,0 +1,31 @@ +#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::tuple, std::map>> Build(); + +private: + const std::map &source; + const IFindParents &inheritance; + std::map> processedNodes; + std::shared_ptr rootNode; + + bool hasParent(const PgClass &cls) const; + std::shared_ptr addNode(const PgClass &cls); + 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/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. diff --git a/tests/pglabtests/TableTreeBuilderTests.cpp b/tests/pglabtests/TableTreeBuilderTests.cpp new file mode 100644 index 0000000..4805024 --- /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, index] = 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, index] = 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, index] = 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 \