#include "TablesTableModel.h" #include "OpenDatabase.h" #include "ScopeGuard.h" #include "catalog/PgDatabaseCatalog.h" #include "catalog/PgClass.h" #include "catalog/PgClassContainer.h" #include "catalog/PgNamespace.h" #include "catalog/PgNamespaceContainer.h" #include "Pgsql_declare.h" #include "CustomDataRole.h" #include #include #include "Pgsql_Connection.h" TablesTableModel::TablesTableModel(std::shared_ptr opendatabase, QObject *parent) : QAbstractTableModel(parent) , openDatabase(opendatabase) {} void TablesTableModel::setNamespaceFilter(NamespaceFilter nsf) { m_namespaceFilter = nsf; refresh(); } void TablesTableModel::setCatalog(std::shared_ptr cat) { if (cat != m_catalog) { m_catalog = cat; refreshConnection = connect(m_catalog.get(), &PgDatabaseCatalog::refreshed, this, &TablesTableModel::refresh); } refresh(); } 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; } } void TablesTableModel::refresh() { beginResetModel(); SCOPE_EXIT { endResetModel(); }; if (!m_catalog) return; // Later afscheiden naar filter functie 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); } } StartLoadTableSizes(std::move(oidIndex)); } QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { switch (section) { case NameCol: return tr("Name"); case NamespaceCol: return tr("Schema"); case KindCol: return tr("Kind"); case OwnerCol: return tr("Owner"); case TablespaceCol: return tr("Tablespace"); case OptionsCol: return tr("Options"); case AclCol: return tr("ACL"); case CommentCol: return tr("Comment"); case TotalSizeCol: return tr("Total size"); case TableSizeCol: return tr("Table size"); case IndexSizeCol: return tr("Index size"); case ToastSizeCol: return tr("TOAST size"); } } } return QVariant(); } int TablesTableModel::rowCount(const QModelIndex &) const { return static_cast(m_tables.size()); } int TablesTableModel::columnCount(const QModelIndex &) const { return colCount; } Oid TablesTableModel::getType(int column) const { Oid oid; switch (column) { case TotalSizeCol: case TableSizeCol: case IndexSizeCol: case ToastSizeCol: oid = Pgsql::int8_oid; break; case TablespaceCol: case OwnerCol: case NameCol: case NamespaceCol: case KindCol: case OptionsCol: case AclCol: case CommentCol: default: oid = Pgsql::varchar_oid; } return oid; } QVariant TablesTableModel::getData(const QModelIndex &index) const { const auto &table = m_tables[index.row()]; 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; } return QVariant(); } PgClass TablesTableModel::getTable(int row) const { return m_tables[row]._class; } Oid TablesTableModel::getTableOid(int row) const { return m_tables[row]._class.oid(); } QVariant TablesTableModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole) return getData(index); else if (role == CustomDataTypeRole) return getType(index.column()); else if (role == CustomDataMeaningRole) { switch (index.column()) { case TotalSizeCol: case TableSizeCol: case IndexSizeCol: case ToastSizeCol: return static_cast(DataMeaningBytes); default: return static_cast(DataMeaningNormal); } } return QVariant(); } void TablesTableModel::StartLoadTableSizes(std::map oidIndex) { QPointer p(this); QtConcurrent::run([this] { return QueryTableSizes(); }) .then(qApp, [p, oidIndex] (TableSizes sizes) { if (p) p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes)); }); } 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; } 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) ); }