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 \