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.
This commit is contained in:
eelke 2023-01-22 15:57:22 +01:00
parent ccd88d0578
commit 39dbab4d36
17 changed files with 452 additions and 127 deletions

View file

@ -38,9 +38,9 @@ public:
virtual Oid getType(int column) const override; virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) 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: protected:
virtual QVariant getDataMeaning(const QModelIndex &index) const override; virtual QVariant getDataMeaning(const QModelIndex &index) const override;

View file

@ -5,15 +5,16 @@
#include "catalog/PgClass.h" #include "catalog/PgClass.h"
#include "catalog/PgClassContainer.h" #include "catalog/PgClassContainer.h"
#include "catalog/PgNamespace.h" #include "catalog/PgNamespace.h"
#include "catalog/PgNamespaceContainer.h" #include "catalog/PgInheritsContainer.h"
#include "Pgsql_declare.h" //#include "Pgsql_declare.h"
#include "ui/catalog/tables/TableTreeBuilder.h"
#include "CustomDataRole.h" #include "CustomDataRole.h"
#include <QBrush> #include <QBrush>
#include <QtConcurrent> #include <QtConcurrent>
#include "Pgsql_Connection.h" #include "Pgsql_Connection.h"
TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent) TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent)
: QAbstractTableModel(parent) : QAbstractItemModel(parent)
, openDatabase(opendatabase) , openDatabase(opendatabase)
{} {}
@ -36,17 +37,18 @@ void TablesTableModel::setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat)
bool TableLike(RelKind relkind) bool TableLike(RelKind relkind)
{ {
switch (relkind) { switch (relkind) {
case RelKind::Table: case RelKind::Table:
case RelKind::View: case RelKind::View:
case RelKind::MaterializedView: case RelKind::MaterializedView:
case RelKind::ForeignTable: case RelKind::ForeignTable:
case RelKind::PartitionedTable: case RelKind::PartitionedTable:
return true; return true;
default: default:
return false; return false;
} }
} }
void TablesTableModel::refresh() void TablesTableModel::refresh()
{ {
beginResetModel(); beginResetModel();
@ -59,27 +61,17 @@ void TablesTableModel::refresh()
auto classes = m_catalog->classes(); auto classes = m_catalog->classes();
std::map<Oid, int> oidIndex; std::map<Oid, int> oidIndex;
m_tables.clear(); rootNode.reset();
for (const auto &e : *classes) {
bool add = false; auto nsfilter = GetNamespaceFilterLambda();
if (TableLike(e.kind)) { std::map<Oid, PgClass> temp;
switch (m_namespaceFilter) { for (const auto &e : *classes)
case NamespaceFilter::User: if (TableLike(e.kind) && nsfilter(e))
add = !e.ns().isSystemCatalog(); temp.emplace(e.oid(), e);
break;
case NamespaceFilter::PgCatalog: TableTreeBuilder treeBuilder(temp, *m_catalog->inherits());
add = e.ns().objectName() == "pg_catalog"; rootNode = treeBuilder.Build();
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)); StartLoadTableSizes(std::move(oidIndex));
} }
@ -106,14 +98,46 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation,
return QVariant(); return QVariant();
} }
int TablesTableModel::rowCount(const QModelIndex &) const int TablesTableModel::rowCount(const QModelIndex &parent) const
{ {
return static_cast<int>(m_tables.size()); if (parent.isValid())
{
const TableNode *parentItem = static_cast<const TableNode*>(parent.internalPointer());
return static_cast<int>(parentItem->children.size());
}
if (rootNode != nullptr)
return static_cast<int>(rootNode->children.size());
return 0;
} }
int TablesTableModel::columnCount(const QModelIndex &) const 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<const TableNode*>(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<const TableNode*>(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 Oid TablesTableModel::getType(int column) const
@ -142,36 +166,38 @@ Oid TablesTableModel::getType(int column) const
QVariant TablesTableModel::getData(const QModelIndex &index) const QVariant TablesTableModel::getData(const QModelIndex &index) const
{ {
const auto &table = m_tables[index.row()]; // const auto &table = rootNode->children[index.row()];
const auto &t = table._class; const TableNode* table = nodeFromIndex(index);
const auto &s = table.sizes; const auto &t = table->_class;
const auto &s = table->sizes;
switch (index.column()) { switch (index.column()) {
case NameCol: return t.objectName(); case NameCol: return t.objectName();
case NamespaceCol: return t.nsName(); case NamespaceCol: return t.nsName();
case KindCol: return t.typeName(); case KindCol: return t.typeName();
case OwnerCol: return t.ownerName(); case OwnerCol: return t.ownerName();
case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace); case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace);
case OptionsCol: break; case OptionsCol: break;
case AclCol: return t.aclString(); case AclCol: return t.aclString();
case CommentCol: return t.description; case CommentCol: return t.description;
case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes; case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes; case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes; case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes; case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
} }
return QVariant(); return QVariant();
} }
PgClass TablesTableModel::getTable(int row) const //PgClass TablesTableModel::getTable(int row) const
{ //{
return m_tables[row]._class; // return rootNode->children[row]->_class;
} //}
Oid TablesTableModel::getTableOid(int row) const //Oid TablesTableModel::getTableOid(int row) const
{ //{
return m_tables[row]._class.oid(); // return rootNode->children[row]->_class.oid();
} //}
QVariant TablesTableModel::data(const QModelIndex &index, int role) const QVariant TablesTableModel::data(const QModelIndex &index, int role) const
{ {
@ -256,16 +282,36 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const
void TablesTableModel::PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes) void TablesTableModel::PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes)
{ {
for (auto s : sizes) // for (auto s : sizes)
{ // {
auto findIter = oidIndex.find(s.oid); // auto findIter = oidIndex.find(s.oid);
if (findIter != oidIndex.end()) // if (findIter != oidIndex.end())
{ // {
m_tables[findIter->second].sizes = s; // rootNode->children[findIter->second]->sizes = s;
} // }
} // }
emit dataChanged( // emit dataChanged(
createIndex(0, TotalSizeCol), // createIndex(0, TotalSizeCol),
createIndex(static_cast<int>(m_tables.size()), ToastSizeCol) // createIndex(static_cast<int>(rootNode->children.size()), ToastSizeCol)
); // );
}
const TableNode* TablesTableModel::nodeFromIndex(const QModelIndex &index)
{
return static_cast<const TableNode*>(index.internalPointer());
}
std::function<bool (const PgClass &)> 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"; };
}
} }

View file

@ -1,9 +1,12 @@
#ifndef TABLESTABLEMODEL_H #ifndef TABLESTABLEMODEL_H
#define 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 "NamespaceFilter.h"
#include "catalog/PgClass.h" #include "catalog/PgClass.h"
#include <QAbstractItemModel>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -11,7 +14,7 @@ class OpenDatabase;
class PgClass; class PgClass;
class PgDatabaseCatalog; class PgDatabaseCatalog;
class TablesTableModel: public QAbstractTableModel { class TablesTableModel: public QAbstractItemModel {
public: public:
using RowItem = PgClass; using RowItem = PgClass;
enum e_Columns : int { enum e_Columns : int {
@ -40,42 +43,34 @@ public:
int rowCount(const QModelIndex &parent) const override; int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override; int columnCount(const QModelIndex &parent) const override;
virtual QVariant data(const QModelIndex &index, int role) const override; QModelIndex index(int row, int column,
PgClass getTable(int row) const; const QModelIndex &parent = QModelIndex()) const override;
RowItem rowItem(int row) const
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: 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<TableSize>; using TableSizes = std::vector<TableSize>;
class Table { // this is a readonly model so when the vectors have been filled
public: // they will not change anymore and it is safe to assume the indexes
PgClass _class; // of elements do not change
TableSize sizes;
Table(const PgClass &cls)
: _class(cls)
{}
};
using t_Tables = std::vector<Table>;
std::shared_ptr<const PgDatabaseCatalog> m_catalog; std::shared_ptr<const PgDatabaseCatalog> m_catalog;
NamespaceFilter m_namespaceFilter = NamespaceFilter::User; NamespaceFilter m_namespaceFilter = NamespaceFilter::User;
t_Tables m_tables; //t_Tables m_tables;
std::shared_ptr<TableNode> rootNode;
QMetaObject::Connection refreshConnection; QMetaObject::Connection refreshConnection;
std::shared_ptr<OpenDatabase> openDatabase; std::shared_ptr<OpenDatabase> openDatabase;
@ -87,6 +82,8 @@ private:
TableSizes QueryTableSizes() const; TableSizes QueryTableSizes() const;
void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes); void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes);
std::function<bool(const PgClass&)> GetNamespaceFilterLambda();
private slots: private slots:
void refresh(); void refresh();

View file

@ -8,7 +8,7 @@
#include "catalog/widgets/TriggerPage.h" #include "catalog/widgets/TriggerPage.h"
#include "catalog/models/ColumnTableModel.h" #include "catalog/models/ColumnTableModel.h"
#include "catalog/models/ConstraintModel.h" #include "catalog/models/ConstraintModel.h"
#include "catalog/models/TablesTableModel.h" #include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableView.h" #include "util/PgLabTableView.h"
#include "ResultTableModelUtil.h" #include "ResultTableModelUtil.h"
#include "widgets/SqlCodePreview.h" #include "widgets/SqlCodePreview.h"
@ -106,7 +106,7 @@ void CatalogTablesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
void CatalogTablesPage::setNamespaceFilter(NamespaceFilter filter) void CatalogTablesPage::setNamespaceFilter(NamespaceFilter filter)
{ {
m_tablesTableView.dataModel()->setNamespaceFilter(filter); m_tablesTableView.dataModel()->setNamespaceFilter(filter);
m_tablesTableView.tableView()->resizeColumnsToContents(); //m_tablesTableView.tableView()->resizeColumnsToContents();
} }
void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous) void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
@ -114,8 +114,8 @@ void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &curr
if (current.row() == previous.row()) if (current.row() == previous.row())
return; return;
auto table = m_tablesTableView.rowItemForProxyIndex(current); auto tableNode = TablesTableModel::nodeFromIndex(m_tablesTableView.sortFilter()->mapToSource(current)); // m_tablesTableView.rowItemForProxyIndex(current);
selectedTableChanged(table); selectedTableChanged(tableNode->_class);
} }
@ -127,11 +127,15 @@ void CatalogTablesPage::tableListTable_layoutChanged(const QList<QPersistentMode
void CatalogTablesPage::on_tableListTable_doubleClicked(const QModelIndex &index) void CatalogTablesPage::on_tableListTable_doubleClicked(const QModelIndex &index)
{ {
auto row = m_tablesTableView.sortFilter()->mapToSource(index).row(); auto sourceIndex = m_tablesTableView.sortFilter()->mapToSource(index);
PgClass table = m_tablesTableView.dataModel()->getTable(row); auto tableNode = TablesTableModel::nodeFromIndex(sourceIndex);
if (table.oid() != InvalidOid) { if (tableNode)
tableSelected(table.oid()); {
} Oid oid = tableNode->_class.oid();
if (oid != InvalidOid) {
tableSelected(oid);
}
}
} }
void CatalogTablesPage::selectedTableChanged(const std::optional<PgClass> &table) void CatalogTablesPage::selectedTableChanged(const std::optional<PgClass> &table)

View file

@ -2,13 +2,13 @@
#define CATALOGTABLESPAGE_H #define CATALOGTABLESPAGE_H
#include "NamespaceFilter.h" #include "NamespaceFilter.h"
#include "catalog/models/TablesTableModel.h" #include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableViewHelper.h" #include "util/PgLabTableViewHelper.h"
#include <QSplitter> #include <QSplitter>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "Pgsql_oids.h"
class CatalogConstraintPage; class CatalogConstraintPage;
class CatalogIndexPage; class CatalogIndexPage;
@ -39,7 +39,7 @@ public:
signals: signals:
void tableSelected(Oid tableoid); void tableSelected(Oid tableoid);
private: private:
PgLabTableViewHelper<TablesTableModel> m_tablesTableView; PgLabTableViewHelper<TablesTableModel, QTreeView> m_tablesTableView;
// Details // Details
QTabWidget *m_detailsTabs = nullptr; QTabWidget *m_detailsTabs = nullptr;

View file

@ -32,8 +32,8 @@ SOURCES += main.cpp\
catalog/models/ProcTableModel.cpp \ catalog/models/ProcTableModel.cpp \
catalog/models/RolesTableModel.cpp \ catalog/models/RolesTableModel.cpp \
catalog/models/SequenceModel.cpp \ catalog/models/SequenceModel.cpp \
catalog/models/TablesTableModel.cpp \
catalog/models/TriggerTableModel.cpp \ catalog/models/TriggerTableModel.cpp \
catalog/tables/TablesTableModel.cpp \
catalog/widgets/CatalogConstraintPage.cpp \ catalog/widgets/CatalogConstraintPage.cpp \
catalog/widgets/CatalogFunctionsPage.cpp \ catalog/widgets/CatalogFunctionsPage.cpp \
catalog/widgets/CatalogIndexPage.cpp \ catalog/widgets/CatalogIndexPage.cpp \
@ -104,8 +104,8 @@ HEADERS += \
catalog/models/ProcTableModel.h \ catalog/models/ProcTableModel.h \
catalog/models/RolesTableModel.h \ catalog/models/RolesTableModel.h \
catalog/models/SequenceModel.h \ catalog/models/SequenceModel.h \
catalog/models/TablesTableModel.h \
catalog/models/TriggerTableModel.h \ catalog/models/TriggerTableModel.h \
catalog/tables/TablesTableModel.h \
catalog/widgets/CatalogConstraintPage.h \ catalog/widgets/CatalogConstraintPage.h \
catalog/widgets/CatalogFunctionsPage.h \ catalog/widgets/CatalogFunctionsPage.h \
catalog/widgets/CatalogIndexPage.h \ catalog/widgets/CatalogIndexPage.h \

View file

@ -1,19 +1,34 @@
#pragma once #pragma once
#include <QTableWidget> #include <QTableWidget>
#include <QTreeView>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include "util/PgLabTableView.h" #include "util/PgLabTableView.h"
#include <optional> #include <optional>
class PgDatabaseCatalog; class PgDatabaseCatalog;
template <typename TableModel> template <typename ViewType>
void ResizeColumnsToContent(ViewType *vt)
{
vt->resizeColumnsToContents();
}
template <>
inline void ResizeColumnsToContent<QTreeView>(QTreeView *)
{
}
template <typename TableModel, typename ViewType = PgLabTableView>
class PgLabTableViewHelper { class PgLabTableViewHelper {
public: public:
PgLabTableViewHelper(QWidget * parent, TableModel *tableModel) PgLabTableViewHelper(QWidget * parent, TableModel *tableModel)
{ {
m_tableView = new PgLabTableView(parent); m_tableView = new ViewType(parent);
m_dataModel = tableModel; m_dataModel = tableModel;
m_sortFilter = new QSortFilterProxyModel(parent); m_sortFilter = new QSortFilterProxyModel(parent);
@ -27,7 +42,7 @@ public:
: PgLabTableViewHelper(parent, new TableModel(parent)) : PgLabTableViewHelper(parent, new TableModel(parent))
{} {}
PgLabTableView *tableView() const ViewType *tableView() const
{ {
return m_tableView; return m_tableView;
} }
@ -45,7 +60,7 @@ public:
void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat) void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{ {
m_dataModel->setCatalog(cat); m_dataModel->setCatalog(cat);
m_tableView->resizeColumnsToContents(); ResizeColumnsToContent(m_tableView);
} }
QModelIndex currentSourceIndex() const QModelIndex currentSourceIndex() const
@ -57,15 +72,15 @@ public:
return m_sortFilter->mapToSource(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) typename TableModel::RowItem rowItemForProxyIndex(QModelIndex index)
{ {
QModelIndex sourceIndex = m_sortFilter->mapToSource(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 typename TableModel::RowItem rowItemForProxyRow(int row) const
@ -79,12 +94,12 @@ public:
if (!index.isValid()) if (!index.isValid())
return {}; return {};
return rowItemForSourceRow(index.row()); return rowItemForSourceRow(index);
} }
private: private:
PgLabTableView *m_tableView = nullptr; ViewType *m_tableView = nullptr;
TableModel *m_dataModel = nullptr; TableModel *m_dataModel = nullptr;
QSortFilterProxyModel *m_sortFilter = nullptr; QSortFilterProxyModel *m_sortFilter = nullptr;
}; };

View file

@ -4,17 +4,24 @@
#include "PgContainer.h" #include "PgContainer.h"
#include "PgInherits.h" #include "PgInherits.h"
#include "Pgsql_declare.h"
class IFindParents
{
public:
virtual std::vector<Oid> getParentsOf(Oid oid) const = 0;
};
class PgInheritsContainer : public PgContainer<PgInherits, PgInherits::Key> { class PgInheritsContainer
: public PgContainer<PgInherits, PgInherits::Key>
, public IFindParents
{
public: public:
using PgContainer<PgInherits, PgInherits::Key>::PgContainer; using PgContainer<PgInherits, PgInherits::Key>::PgContainer;
virtual std::string getLoadQuery() const override; virtual std::string getLoadQuery() const override;
/// Returns the parents in the correct order /// Returns the parents in the correct order
std::vector<Oid> getParentsOf(Oid oid) const; std::vector<Oid> getParentsOf(Oid oid) const override;
protected: protected:
PgInherits loadElem(const Pgsql::Row &row) override; PgInherits loadElem(const Pgsql::Row &row) override;
}; };

View file

@ -45,6 +45,9 @@ SOURCES += \
catalog/PgConstraintContainer.cpp \ catalog/PgConstraintContainer.cpp \
ParamListJson.cpp \ ParamListJson.cpp \
ParamListModel.cpp \ ParamListModel.cpp \
ui/catalog/tables/TableNode.cpp \
ui/catalog/tables/TableSize.cpp \
ui/catalog/tables/TableTreeBuilder.cpp \
util.cpp \ util.cpp \
SqlFormattingUtils.cpp \ SqlFormattingUtils.cpp \
catalog/PgKeywordList.cpp \ catalog/PgKeywordList.cpp \
@ -113,6 +116,9 @@ HEADERS += \
catalog/PgConstraintContainer.h \ catalog/PgConstraintContainer.h \
ParamListJson.h \ ParamListJson.h \
ParamListModel.h \ ParamListModel.h \
ui/catalog/tables/TableNode.h \
ui/catalog/tables/TableSize.h \
ui/catalog/tables/TableTreeBuilder.h \
util.h \ util.h \
SqlFormattingUtils.h \ SqlFormattingUtils.h \
catalog/PgCatalogTypes.h \ catalog/PgCatalogTypes.h \

View file

@ -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();
}

View file

@ -0,0 +1,25 @@
#pragma once
#include "TableSize.h"
#include "catalog/PgClass.h"
class TableNode;
using t_Tables = std::vector<std::shared_ptr<TableNode>>;
class TableNode {
public:
PgClass _class;
TableSize sizes;
int myIndex;
std::weak_ptr<TableNode> 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;
};

View file

@ -0,0 +1,6 @@
#include "TableSize.h"
TableSize::TableSize()
: oid(0)
{}

View file

@ -0,0 +1,13 @@
#pragma once
#include <cstdint>
class TableSize {
public:
int oid;
int64_t totalBytes = -1;
int64_t indexBytes = -1;
int64_t toastBytes = -1;
TableSize();
};

View file

@ -0,0 +1,89 @@
#include "TableTreeBuilder.h"
#include "catalog/PgInheritsContainer.h"
#include "TableNode.h"
#include <ranges>
TableTreeBuilder::TableTreeBuilder(
const std::map<Oid, PgClass> &source,
const IFindParents &inheritance
)
: source(source)
, inheritance(inheritance)
{
}
std::shared_ptr<TableNode> TableTreeBuilder::Build()
{
rootNode = std::make_shared<TableNode>();
// 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<TableNode> TableTreeBuilder::addNode(const PgClass &cls)
{
std::shared_ptr<TableNode> node = std::make_shared<TableNode>(cls);
processedNodes.emplace(cls.oid(), node);
std::vector<Oid> parents = inheritance.getParentsOf(cls.oid());
if (parents.empty())
addToplevelNode(node);
else
addToParents(node, parents);
return node;
}
void TableTreeBuilder::addToplevelNode(std::shared_ptr<TableNode> node)
{
rootNode->children.push_back(node);
}
void TableTreeBuilder::addToParents(std::shared_ptr<TableNode> node, const std::vector<Oid> &parents)
{
for (auto &parent : parents)
{
getParent(parent)->children.push_back(node);
}
}
std::shared_ptr<TableNode> 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<TableNode> parent)
{
int index = 0;
for (auto &n : parent->children)
{
n->parent = parent;
n->myIndex = index++;
AssignNodeIndexesAndParents(n);
}
}

View file

@ -0,0 +1,32 @@
#pragma once
#include "Pgsql_oids.h"
#include <map>
#include <memory>
class PgClass;
class TableNode;
class IFindParents;
class TableTreeBuilder
{
public:
TableTreeBuilder(const std::map<Oid, PgClass> &source, const IFindParents &inheritance);
std::shared_ptr<TableNode> Build();
private:
const std::map<Oid, PgClass> &source;
const IFindParents &inheritance;
std::map<Oid, std::shared_ptr<TableNode>> processedNodes;
std::shared_ptr<TableNode> rootNode;
void resetState();
bool hasParent(const PgClass &cls) const;
std::shared_ptr<TableNode> addNode(const PgClass &cls);
void addToplevelNode(std::shared_ptr<TableNode> node);
void addToParents(std::shared_ptr<TableNode> node, const std::vector<Oid> &parents);
std::shared_ptr<TableNode> getParent(Oid oid);
void AssignNodeIndexesAndParents(std::shared_ptr<TableNode> parent);
};

View file

@ -0,0 +1,64 @@
#include <gtest/gtest.h>
#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<Oid, PgClass> 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<Oid, PgClass> 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<Oid> getParentsOf(Oid oid) const override
{
if (oid == 1001)
return { 1002 };
return {};
}
};
TEST(TableTreeBuilder, WithChild)
{
std::map<Oid, PgClass> 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]);
}

View file

@ -14,6 +14,7 @@ QT += core widgets
HEADERS += HEADERS +=
SOURCES += main.cpp \ SOURCES += main.cpp \
TableTreeBuilderTests.cpp \
tst_ConvertLangToSqlString.cpp \ tst_ConvertLangToSqlString.cpp \
tst_ConvertToMultiLineCString.cpp \ tst_ConvertToMultiLineCString.cpp \
tst_ExplainJsonParser.cpp \ tst_ExplainJsonParser.cpp \