2017-12-10 10:35:46 +01:00
|
|
|
|
#include "TablesTableModel.h"
|
2022-01-17 17:12:07 +01:00
|
|
|
|
#include "OpenDatabase.h"
|
2019-10-09 18:36:54 +02:00
|
|
|
|
#include "ScopeGuard.h"
|
2018-12-16 10:17:59 +01:00
|
|
|
|
#include "catalog/PgDatabaseCatalog.h"
|
|
|
|
|
|
#include "catalog/PgClass.h"
|
|
|
|
|
|
#include "catalog/PgClassContainer.h"
|
|
|
|
|
|
#include "catalog/PgNamespace.h"
|
2023-01-22 15:57:22 +01:00
|
|
|
|
#include "catalog/PgInheritsContainer.h"
|
|
|
|
|
|
//#include "Pgsql_declare.h"
|
|
|
|
|
|
#include "ui/catalog/tables/TableTreeBuilder.h"
|
2018-09-02 10:30:30 +00:00
|
|
|
|
#include "CustomDataRole.h"
|
2017-12-12 20:13:53 +01:00
|
|
|
|
#include <QBrush>
|
2022-01-17 17:12:07 +01:00
|
|
|
|
#include <QtConcurrent>
|
|
|
|
|
|
#include "Pgsql_Connection.h"
|
2017-12-10 10:35:46 +01:00
|
|
|
|
|
2022-01-17 17:12:07 +01:00
|
|
|
|
TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent)
|
2023-01-22 15:57:22 +01:00
|
|
|
|
: QAbstractItemModel(parent)
|
2022-01-17 17:12:07 +01:00
|
|
|
|
, openDatabase(opendatabase)
|
2018-09-02 10:30:30 +00:00
|
|
|
|
{}
|
2017-12-10 10:35:46 +01:00
|
|
|
|
|
2018-12-29 10:56:24 +01:00
|
|
|
|
void TablesTableModel::setNamespaceFilter(NamespaceFilter nsf)
|
|
|
|
|
|
{
|
|
|
|
|
|
m_namespaceFilter = nsf;
|
2019-10-09 18:36:54 +02:00
|
|
|
|
refresh();
|
2018-12-29 10:56:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-10 10:35:46 +01:00
|
|
|
|
void TablesTableModel::setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat)
|
|
|
|
|
|
{
|
2019-10-09 18:36:54 +02:00
|
|
|
|
if (cat != m_catalog) {
|
|
|
|
|
|
m_catalog = cat;
|
|
|
|
|
|
refreshConnection = connect(m_catalog.get(), &PgDatabaseCatalog::refreshed,
|
|
|
|
|
|
this, &TablesTableModel::refresh);
|
|
|
|
|
|
}
|
|
|
|
|
|
refresh();
|
2018-12-29 10:56:24 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-07 07:44:33 +01:00
|
|
|
|
bool TableLike(RelKind relkind)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (relkind) {
|
2023-01-22 15:57:22 +01:00
|
|
|
|
case RelKind::Table:
|
|
|
|
|
|
case RelKind::View:
|
|
|
|
|
|
case RelKind::MaterializedView:
|
|
|
|
|
|
case RelKind::ForeignTable:
|
|
|
|
|
|
case RelKind::PartitionedTable:
|
|
|
|
|
|
return true;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return false;
|
2023-01-07 07:44:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-22 15:57:22 +01:00
|
|
|
|
|
2019-10-09 18:36:54 +02:00
|
|
|
|
void TablesTableModel::refresh()
|
2018-12-29 10:56:24 +01:00
|
|
|
|
{
|
2019-10-09 18:36:54 +02:00
|
|
|
|
beginResetModel();
|
|
|
|
|
|
SCOPE_EXIT { endResetModel(); };
|
|
|
|
|
|
|
2018-12-29 10:56:24 +01:00
|
|
|
|
if (!m_catalog)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
auto classes = m_catalog->classes();
|
2023-01-22 15:57:22 +01:00
|
|
|
|
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());
|
2023-01-22 16:46:14 +01:00
|
|
|
|
auto [rn, oidIndex] = treeBuilder.Build();
|
|
|
|
|
|
rootNode = rn;
|
2023-01-22 15:57:22 +01:00
|
|
|
|
|
2022-01-17 17:12:07 +01:00
|
|
|
|
StartLoadTableSizes(std::move(oidIndex));
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (orientation == Qt::Horizontal) {
|
|
|
|
|
|
if (role == Qt::DisplayRole) {
|
|
|
|
|
|
switch (section) {
|
2018-11-25 09:06:01 +01:00
|
|
|
|
case NameCol: return tr("Name");
|
|
|
|
|
|
case NamespaceCol: return tr("Schema");
|
2018-12-29 11:19:12 +01:00
|
|
|
|
case KindCol: return tr("Kind");
|
2018-11-25 09:06:01 +01:00
|
|
|
|
case OwnerCol: return tr("Owner");
|
|
|
|
|
|
case TablespaceCol: return tr("Tablespace");
|
|
|
|
|
|
case OptionsCol: return tr("Options");
|
|
|
|
|
|
case AclCol: return tr("ACL");
|
2021-03-08 16:59:13 +01:00
|
|
|
|
case CommentCol: return tr("Comment");
|
2022-01-17 17:12:07 +01:00
|
|
|
|
case TotalSizeCol: return tr("Total size");
|
|
|
|
|
|
case TableSizeCol: return tr("Table size");
|
|
|
|
|
|
case IndexSizeCol: return tr("Index size");
|
|
|
|
|
|
case ToastSizeCol: return tr("TOAST size");
|
2021-03-10 19:06:40 +01:00
|
|
|
|
}
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2018-11-25 09:06:01 +01:00
|
|
|
|
return QVariant();
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-22 15:57:22 +01:00
|
|
|
|
int TablesTableModel::rowCount(const QModelIndex &parent) const
|
2017-12-10 10:35:46 +01:00
|
|
|
|
{
|
2023-01-22 15:57:22 +01:00
|
|
|
|
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;
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int TablesTableModel::columnCount(const QModelIndex &) const
|
|
|
|
|
|
{
|
2023-01-22 15:57:22 +01:00
|
|
|
|
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 {};
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Oid TablesTableModel::getType(int column) const
|
|
|
|
|
|
{
|
|
|
|
|
|
Oid oid;
|
|
|
|
|
|
switch (column) {
|
2022-01-17 17:12:07 +01:00
|
|
|
|
case TotalSizeCol:
|
|
|
|
|
|
case TableSizeCol:
|
|
|
|
|
|
case IndexSizeCol:
|
|
|
|
|
|
case ToastSizeCol:
|
2021-03-10 19:06:40 +01:00
|
|
|
|
oid = Pgsql::int8_oid;
|
|
|
|
|
|
break;
|
2017-12-10 10:35:46 +01:00
|
|
|
|
case TablespaceCol:
|
|
|
|
|
|
case OwnerCol:
|
|
|
|
|
|
case NameCol:
|
2017-12-12 20:13:53 +01:00
|
|
|
|
case NamespaceCol:
|
2018-12-29 11:19:12 +01:00
|
|
|
|
case KindCol:
|
2017-12-10 10:35:46 +01:00
|
|
|
|
case OptionsCol:
|
2018-11-25 09:06:01 +01:00
|
|
|
|
case AclCol:
|
2021-03-08 16:59:13 +01:00
|
|
|
|
case CommentCol:
|
2017-12-10 10:35:46 +01:00
|
|
|
|
default:
|
2018-09-09 18:52:32 +02:00
|
|
|
|
oid = Pgsql::varchar_oid;
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
return oid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QVariant TablesTableModel::getData(const QModelIndex &index) const
|
|
|
|
|
|
{
|
2023-01-22 15:57:22 +01:00
|
|
|
|
// const auto &table = rootNode->children[index.row()];
|
|
|
|
|
|
const TableNode* table = nodeFromIndex(index);
|
|
|
|
|
|
const auto &t = table->_class;
|
|
|
|
|
|
const auto &s = table->sizes;
|
|
|
|
|
|
|
2017-12-10 10:35:46 +01:00
|
|
|
|
switch (index.column()) {
|
2023-01-22 15:57:22 +01:00
|
|
|
|
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;
|
2021-03-10 19:06:40 +01:00
|
|
|
|
}
|
2017-12-10 10:35:46 +01:00
|
|
|
|
|
2018-11-25 09:06:01 +01:00
|
|
|
|
return QVariant();
|
2017-12-10 10:35:46 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-12 20:13:53 +01:00
|
|
|
|
QVariant TablesTableModel::data(const QModelIndex &index, int role) const
|
|
|
|
|
|
{
|
2018-09-02 10:30:30 +00:00
|
|
|
|
if (role == Qt::DisplayRole)
|
|
|
|
|
|
return getData(index);
|
|
|
|
|
|
else if (role == CustomDataTypeRole)
|
|
|
|
|
|
return getType(index.column());
|
2022-08-17 18:18:10 +02:00
|
|
|
|
else if (role == CustomDataMeaningRole)
|
|
|
|
|
|
{
|
2021-03-10 19:06:40 +01:00
|
|
|
|
switch (index.column()) {
|
2022-01-17 17:12:07 +01:00
|
|
|
|
case TotalSizeCol:
|
|
|
|
|
|
case TableSizeCol:
|
|
|
|
|
|
case IndexSizeCol:
|
|
|
|
|
|
case ToastSizeCol:
|
2021-03-10 19:06:40 +01:00
|
|
|
|
return static_cast<int>(DataMeaningBytes);
|
|
|
|
|
|
default:
|
|
|
|
|
|
return static_cast<int>(DataMeaningNormal);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-09-02 10:30:30 +00:00
|
|
|
|
return QVariant();
|
2017-12-12 20:13:53 +01:00
|
|
|
|
}
|
2022-01-17 17:12:07 +01:00
|
|
|
|
|
2023-01-22 16:46:14 +01:00
|
|
|
|
void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
|
2022-01-17 17:12:07 +01:00
|
|
|
|
{
|
|
|
|
|
|
QPointer p(this);
|
|
|
|
|
|
QtConcurrent::run([this]
|
|
|
|
|
|
{
|
|
|
|
|
|
return QueryTableSizes();
|
|
|
|
|
|
})
|
2022-01-19 19:10:05 +01:00
|
|
|
|
.then(qApp, [p, oidIndex] (TableSizes sizes)
|
2022-01-17 17:12:07 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (p)
|
2023-01-22 16:46:14 +01:00
|
|
|
|
p.data()->PopulateSizes(oidIndex, sizes);
|
2022-01-17 17:12:07 +01:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const
|
|
|
|
|
|
{
|
|
|
|
|
|
std::string nsfilter;
|
|
|
|
|
|
switch (m_namespaceFilter)
|
|
|
|
|
|
{
|
|
|
|
|
|
case NamespaceFilter::User:
|
|
|
|
|
|
nsfilter = "(NOT (nspname LIKE 'pg_%' OR nspname='information_schema'))";
|
|
|
|
|
|
break;
|
|
|
|
|
|
case NamespaceFilter::PgCatalog:
|
|
|
|
|
|
nsfilter = "nspname = 'pg_catalog'";
|
|
|
|
|
|
break;
|
|
|
|
|
|
case NamespaceFilter::InformationSchema:
|
|
|
|
|
|
nsfilter = "nspname = 'information_schema'";
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::string q =
|
|
|
|
|
|
"SELECT pg_class.oid, "
|
|
|
|
|
|
" pg_total_relation_size(pg_class.oid) AS total_bytes, "
|
|
|
|
|
|
" CASE WHEN relkind='r' THEN pg_indexes_size(pg_class.oid) ELSE 0 END AS index_bytes, "
|
|
|
|
|
|
" CASE WHEN relkind='r' THEN pg_total_relation_size(reltoastrelid) ELSE 0 END AS toast_bytes "
|
|
|
|
|
|
"\nFROM pg_catalog.pg_class \n"
|
|
|
|
|
|
" JOIN pg_namespace ON (relnamespace = pg_namespace.oid)"
|
|
|
|
|
|
" LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n"
|
|
|
|
|
|
"WHERE relkind IN ('r', 'p', 'm') AND " + nsfilter;
|
|
|
|
|
|
|
|
|
|
|
|
;
|
|
|
|
|
|
Pgsql::Connection connection;
|
|
|
|
|
|
connection.connect(openDatabase->config().connectionString());
|
|
|
|
|
|
Pgsql::Result sizesResult = connection.query(q.c_str());
|
|
|
|
|
|
|
|
|
|
|
|
TablesTableModel::TableSizes sizes;
|
|
|
|
|
|
sizes.reserve(sizesResult.rows());
|
|
|
|
|
|
for (const Pgsql::Row& row : sizesResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
TableSize size;
|
|
|
|
|
|
size.oid = row.get(0);
|
|
|
|
|
|
size.totalBytes = row.get(1);
|
|
|
|
|
|
size.indexBytes = row.get(2);
|
|
|
|
|
|
size.toastBytes = row.get(3);
|
|
|
|
|
|
sizes.push_back(size);
|
|
|
|
|
|
}
|
|
|
|
|
|
return sizes;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-22 16:46:14 +01:00
|
|
|
|
void TablesTableModel::PopulateSizes(
|
|
|
|
|
|
const OidClassIndex &oidIndex,
|
|
|
|
|
|
const std::vector<TableSize> &sizes
|
|
|
|
|
|
)
|
2022-01-17 17:12:07 +01:00
|
|
|
|
{
|
2023-01-22 16:46:14 +01:00
|
|
|
|
for (auto s : sizes)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto findIter = oidIndex.find(s.oid);
|
|
|
|
|
|
if (findIter != oidIndex.end())
|
|
|
|
|
|
{
|
|
|
|
|
|
findIter->second->sizes = s;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
emit dataChanged(
|
|
|
|
|
|
index(0, TotalSizeCol),
|
|
|
|
|
|
index(static_cast<int>(rootNode->children.size()), ToastSizeCol)
|
|
|
|
|
|
);
|
2023-01-22 15:57:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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"; };
|
2022-01-17 17:12:07 +01:00
|
|
|
|
}
|
2023-01-22 15:57:22 +01:00
|
|
|
|
|
2022-01-17 17:12:07 +01:00
|
|
|
|
}
|