The table inheritance works mostly #87

Merged
eelke merged 4 commits from table-inheritance-tree into main 2023-01-24 17:47:53 +00:00
22 changed files with 473 additions and 145 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)
{} {}
@ -47,6 +48,7 @@ bool TableLike(RelKind relkind)
} }
} }
void TablesTableModel::refresh() void TablesTableModel::refresh()
{ {
beginResetModel(); beginResetModel();
@ -55,31 +57,19 @@ void TablesTableModel::refresh()
if (!m_catalog) if (!m_catalog)
return; return;
// Later afscheiden naar filter functie
auto classes = m_catalog->classes(); auto classes = m_catalog->classes();
rootNode.reset();
auto nsfilter = GetNamespaceFilterLambda();
std::map<Oid, PgClass> 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<Oid, int> 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)); StartLoadTableSizes(std::move(oidIndex));
} }
@ -106,9 +96,17 @@ 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
@ -116,6 +114,30 @@ 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
{ {
Oid oid; Oid oid;
@ -142,9 +164,11 @@ 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();
@ -163,16 +187,6 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const
return QVariant(); 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 QVariant TablesTableModel::data(const QModelIndex &index, int role) const
{ {
if (role == Qt::DisplayRole) if (role == Qt::DisplayRole)
@ -195,7 +209,7 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const
return QVariant(); return QVariant();
} }
void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex) void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
{ {
QPointer p(this); QPointer p(this);
QtConcurrent::run([this] QtConcurrent::run([this]
@ -205,7 +219,7 @@ void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex)
.then(qApp, [p, oidIndex] (TableSizes sizes) .then(qApp, [p, oidIndex] (TableSizes sizes)
{ {
if (p) 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; return sizes;
} }
void TablesTableModel::PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes) void TablesTableModel::PopulateSizes(
const OidClassIndex &oidIndex,
const 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; findIter->second->sizes = s;
} }
} }
emit dataChanged( emit dataChanged(
createIndex(0, TotalSizeCol), index(0, TotalSizeCol),
createIndex(static_cast<int>(m_tables.size()), ToastSizeCol) index(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,31 @@ 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;
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: 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; std::shared_ptr<TableNode> rootNode;
QMetaObject::Connection refreshConnection; QMetaObject::Connection refreshConnection;
std::shared_ptr<OpenDatabase> openDatabase; std::shared_ptr<OpenDatabase> openDatabase;
@ -83,10 +75,17 @@ private:
Oid getType(int column) const; Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const; QVariant getData(const QModelIndex &index) const;
void StartLoadTableSizes(std::map<Oid, int> oidIndex); using OidClassIndex = std::map<Oid, std::shared_ptr<TableNode>>;
TableSizes QueryTableSizes() const;
void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes);
void StartLoadTableSizes(OidClassIndex oidIndex);
TableSizes QueryTableSizes() const;
void PopulateSizes(
const OidClassIndex &oidIndex,
const 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"
@ -27,7 +27,7 @@ CatalogTablesPage::CatalogTablesPage(std::shared_ptr<OpenDatabase> opendatabase,
: QSplitter(Qt::Horizontal, parent) : QSplitter(Qt::Horizontal, parent)
, m_tablesTableView(this, new TablesTableModel(opendatabase, this)) , m_tablesTableView(this, new TablesTableModel(opendatabase, this))
{ {
auto tv = m_tablesTableView.tableView(); auto tv = m_tablesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection); tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this); m_detailsTabs = new QTabWidget(this);
@ -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,10 +127,14 @@ 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);
}
} }
} }

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

@ -12,7 +12,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget
: QSplitter(Qt::Horizontal, parent) : QSplitter(Qt::Horizontal, parent)
, m_databasesTableView(this, new DatabasesTableModel(opendatabase, this)) , m_databasesTableView(this, new DatabasesTableModel(opendatabase, this))
{ {
auto tv = m_databasesTableView.tableView(); auto tv = m_databasesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection); tv->setSelectionMode(QAbstractItemView::SingleSelection);
tv->setContextMenuPolicy(Qt::ActionsContextMenu); tv->setContextMenuPolicy(Qt::ActionsContextMenu);
@ -23,7 +23,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget
m_tableSql = new SqlCodePreview(this); m_tableSql = new SqlCodePreview(this);
addWidget(m_tableSql); addWidget(m_tableSql);
connect(m_databasesTableView.tableView()->selectionModel(), &QItemSelectionModel::currentRowChanged, connect(m_databasesTableView.itemView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &DatabasesPage::databaseSelectionChanged); this, &DatabasesPage::databaseSelectionChanged);
connect(createDbAction, &QAction::triggered, connect(createDbAction, &QAction::triggered,
[this](auto checked) [this](auto checked)
@ -37,7 +37,7 @@ void DatabasesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{ {
m_catalog = cat; m_catalog = cat;
m_databasesTableView.dataModel()->setDatabaseList(cat); m_databasesTableView.dataModel()->setDatabaseList(cat);
m_databasesTableView.tableView()->resizeColumnsToContents(); m_databasesTableView.itemView()->resizeColumnsToContents();
} }
void DatabasesPage::updateDatabaseDetails(const PgDatabase &db) void DatabasesPage::updateDatabaseDetails(const PgDatabase &db)

View file

@ -6,7 +6,7 @@ RolesPage::RolesPage(QWidget * parent)
: QSplitter(Qt::Horizontal, parent) : QSplitter(Qt::Horizontal, parent)
, m_rolesTableView(this) , m_rolesTableView(this)
{ {
auto tv = m_rolesTableView.tableView(); auto tv = m_rolesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection); tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this); m_detailsTabs = new QTabWidget(this);
@ -19,5 +19,5 @@ void RolesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{ {
m_catalog = cat; m_catalog = cat;
m_rolesTableView.dataModel()->setRoleList(cat->authIds()); m_rolesTableView.dataModel()->setRoleList(cat->authIds());
m_rolesTableView.tableView()->resizeColumnsToContents(); m_rolesTableView.itemView()->resizeColumnsToContents();
} }

View file

@ -135,7 +135,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
suffix = "B"; suffix = "B";
forground_color = QColorConstants::Svg::darkblue; 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 { else {
auto str = value.toString(); auto str = value.toString();

View file

@ -1,25 +1,51 @@
#pragma once #pragma once
#include <QTableWidget> #include <QTableWidget>
#include <QTreeView>
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
#include "util/PgLabItemDelegate.h"
#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 ViewType>
void InitView(ViewType *vt)
{}
template <>
inline void InitView<QTreeView>(QTreeView *tv)
{
tv->setAlternatingRowColors(true);
tv->setItemDelegate(new PgLabItemDelegate(tv));
tv->setWordWrap(false);
}
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_itemView = new ViewType(parent);
InitView(m_itemView);
m_dataModel = tableModel; m_dataModel = tableModel;
m_sortFilter = new QSortFilterProxyModel(parent); m_sortFilter = new QSortFilterProxyModel(parent);
m_sortFilter->setSourceModel(m_dataModel); m_sortFilter->setSourceModel(m_dataModel);
m_tableView->setModel(m_sortFilter); m_itemView->setModel(m_sortFilter);
m_tableView->setSortingEnabled(true); m_itemView->setSortingEnabled(true);
m_sortFilter->sort(0, Qt::AscendingOrder); m_sortFilter->sort(0, Qt::AscendingOrder);
} }
@ -27,9 +53,9 @@ public:
: PgLabTableViewHelper(parent, new TableModel(parent)) : PgLabTableViewHelper(parent, new TableModel(parent))
{} {}
PgLabTableView *tableView() const ViewType *itemView() const
{ {
return m_tableView; return m_itemView;
} }
TableModel *dataModel() const TableModel *dataModel() const
@ -45,27 +71,27 @@ 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_itemView);
} }
QModelIndex currentSourceIndex() const QModelIndex currentSourceIndex() const
{ {
QModelIndex index = m_tableView->selectionModel()->currentIndex(); QModelIndex index = m_itemView->selectionModel()->currentIndex();
if (!index.isValid()) if (!index.isValid())
return index; return index;
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 +105,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_itemView = 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,82 @@
#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::tuple<std::shared_ptr<TableNode>, std::map<Oid, 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);
return { rootNode, std::move(processedNodes) };
}
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())
addToToplevel(node);
else
addToParents(node, parents);
return node;
}
void TableTreeBuilder::addToToplevel(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,31 @@
#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::tuple<std::shared_ptr<TableNode>, std::map<Oid, 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;
bool hasParent(const PgClass &cls) const;
std::shared_ptr<TableNode> addNode(const PgClass &cls);
void addToToplevel(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

@ -25,21 +25,21 @@ namespace Pgsql {
return *this; return *this;
} }
bool operator==(const ResultConstIterator &rhs) bool operator==(const ResultConstIterator &rhs) const
{ {
return m_row == rhs.m_row; return m_row == rhs.m_row;
} }
bool operator!=(const ResultConstIterator &rhs) bool operator!=(const ResultConstIterator &rhs) const
{ {
return !operator==(rhs); return !operator==(rhs);
} }
const Row& operator*() const Row& operator*() const
{ {
return m_row; return m_row;
} }
const Row& operator->() const Row& operator->() const
{ {
return m_row; return m_row;
} }

View file

@ -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.

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, index] = 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, index] = 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, 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]);
}

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 \