Flexible models #76

Merged
eelke merged 9 commits from flexible-models into master 2018-09-02 10:30:31 +00:00
24 changed files with 333 additions and 51 deletions

10
pglab/CustomDataRole.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef CUSTOMDATAROLE_H
#define CUSTOMDATAROLE_H
#include <Qt>
enum CustomDataRole {
CustomDataTypeRole = Qt::UserRole,
};
#endif // CUSTOMDATAROLE_H

View file

@ -3,6 +3,7 @@
#include "PgIndexContainer.h"
#include "Pgsql_oids.h"
#include "ScopeGuard.h"
#include "CustomDataRole.h"
void IndexModel::setData(std::shared_ptr<const PgDatabaseCatalog> cat, const PgClass &table)
{
@ -96,7 +97,7 @@ QVariant IndexModel::data(const QModelIndex &index, int role) const
QVariant v;
if (role == Qt::DisplayRole)
v = getData(index);
else if (role == Qt::UserRole)
else if (role == CustomDataTypeRole)
v = getType(index.column());
return v;
}

View file

@ -83,7 +83,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
Oid oid = InvalidOid;
value = index.data(Qt::UserRole); // get OID
if (value.isValid())
oid = value.toInt(); //getType(index.column());
oid = value.toUInt(); //getType(index.column());
value = index.data(Qt::DisplayRole);
@ -112,7 +112,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
// option->backgroundBrush = qvariant_cast<QBrush>(index.data(Qt::BackgroundRole));
// disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
option->styleObject = 0;
option->styleObject = nullptr;
}
void PgLabItemDelegate::paint(QPainter *painter,

View file

@ -3,6 +3,12 @@
#include <QStyledItemDelegate>
/** Delegate for rendering SQL data types in tableviews
*
* This delegate removes the need for the model to provide formatting information
* which in many cases solely based on the datatype so this delegate can determine
* on its own what the correct formatting is.
*/
class PgLabItemDelegate : public QStyledItemDelegate
{
public:

View file

@ -0,0 +1,104 @@
#include "PropertyProxyModel.h"
/*
* Code borrowed from: https://stackoverflow.com/questions/21653253/how-to-change-orientation-of-qt-tableview
*
* Originally it was called Horizontal_proxy_model however some adjustments were made to it.
* Instead of the column headers becoming row headers we now convert them to the first column.
* The second column show the values of a single row from the source model. Which is determined
* by the setActiveRow call.
*/
PropertyProxyModel::PropertyProxyModel(QObject *parent)
: QIdentityProxyModel(parent)
{
}
QModelIndex PropertyProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
if (sourceModel()) {
if (activeRow >= 0) {
if (proxyIndex.column() == valueColumn) {
return sourceModel()->index(activeRow, proxyIndex.row());
}
}
}
return QModelIndex();
}
QModelIndex PropertyProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
if (activeRow >= 0) {
if (sourceIndex.row() == activeRow) {
return index(sourceIndex.column(), valueColumn);
}
}
return QModelIndex();
}
QModelIndex PropertyProxyModel::index(int row, int column, const QModelIndex &) const
{
return createIndex(row, column, nullptr);
}
QModelIndex PropertyProxyModel::parent(const QModelIndex &) const
{
return QModelIndex();
}
int PropertyProxyModel::rowCount(const QModelIndex &) const
{
return sourceModel() ? sourceModel()->columnCount() : 0;
}
int PropertyProxyModel::columnCount(const QModelIndex &) const
{
return 2;
}
QVariant PropertyProxyModel::headerData(
int section, Qt::Orientation orientation, int role) const
{
// if (!sourceModel()) {
// return QVariant();
// }
// Qt::Orientation new_orientation = orientation == Qt::Horizontal ?
// Qt::Vertical : Qt::Horizontal;
// return sourceModel()->headerData(section, new_orientation, role);
if (orientation == Qt::Horizontal) {
if (role == Qt::DisplayRole) {
switch (section) {
case 0:
return tr("Property");
case 1:
return tr("Value");
}
}
}
return QVariant();
}
QVariant PropertyProxyModel::data(const QModelIndex &proxyIndex, int role) const
{
auto sm = sourceModel();
if (sm) {
switch (proxyIndex.column()) {
case 0:
// return source header data
return sm->headerData(proxyIndex.row(), Qt::Horizontal, role);
case 1:
// return value if activeRow is set
if (activeRow >= 0) {
return QIdentityProxyModel::data(proxyIndex, role);
}
}
}
//return d->model->data(mapToSource(proxyIndex), role);
return QVariant();
}
void PropertyProxyModel::setActiveRow(const QModelIndex &row)
{
activeRow = row.isValid() ? row.row() : -1;
emit dataChanged(index(0, valueColumn), index(rowCount(QModelIndex()), valueColumn), QVector<int>() << Qt::DisplayRole);
}

View file

@ -0,0 +1,35 @@
#ifndef HORIZONTALPROXYMODEL_H
#define HORIZONTALPROXYMODEL_H
#include <QIdentityProxyModel>
class PropertyProxyModel : public QIdentityProxyModel {
Q_OBJECT
public:
PropertyProxyModel(QObject * parent = nullptr);
QModelIndex mapToSource(const QModelIndex &proxyIndex) const;
QModelIndex mapFromSource(const QModelIndex &sourceIndex) const;
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &child) const;
int rowCount(const QModelIndex &parent) const;
int columnCount(const QModelIndex &parent) const;
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
QVariant data(const QModelIndex &proxyIndex, int role) const;
public Q_SLOTS:
/** Updates the model (and view) to show the values for row
*
* The column part of the index is not used QModelIndex is used to make is eacy to connect to
* QItemSelectionModel::currentRowChanged
*/
void setActiveRow(const QModelIndex &row);
private:
enum Columns {
propertyColumn = 0,
valueColumn
};
int activeRow = -1;
};
#endif // HORIZONTALPROXYMODEL_H

View file

@ -7,7 +7,7 @@
#include "ResultTableModelUtil.h"
#include "ColumnTableModel.h"
#include "ConstraintModel.h"
//#include "NamespaceFilterWidget.h"
#include "PropertyProxyModel.h"
#include "IconColumnDelegate.h"
#include "IndexModel.h"
#include "SqlFormattingUtils.h"
@ -24,9 +24,13 @@ TablesPage::TablesPage(MainWindow *parent)
{
ui->setupUi(this);
auto pglab_delegate = new PgLabItemDelegate(this);
auto icon_delegate = new IconColumnDelegate(this);
SetTableViewDefault(ui->tableListTable);
m_tablesModel = new TablesTableModel(this);
ui->tableListTable->setModel(m_tablesModel);
ui->tableListTable->setItemDelegate(pglab_delegate);
ui->tableListTable->setSortingEnabled(true);
ui->tableListTable->sortByColumn(0, Qt::AscendingOrder);
@ -36,9 +40,8 @@ TablesPage::TablesPage(MainWindow *parent)
SetTableViewDefault(ui->constraintsTable);
m_constraintModel = new ConstraintModel(this);
auto delegate = new IconColumnDelegate(this);
ui->constraintsTable->setModel(m_constraintModel);
ui->constraintsTable->setItemDelegateForColumn(0, delegate);
ui->constraintsTable->setItemDelegateForColumn(0, icon_delegate);
QFont font;
font.setFamily("Source Code Pro");
@ -50,8 +53,18 @@ TablesPage::TablesPage(MainWindow *parent)
SetTableViewDefault(ui->indexesTable);
m_indexModel = new IndexModel(this);
ui->indexesTable->setModel(m_indexModel);
ui->indexesTable->setItemDelegate(new PgLabItemDelegate(ui->indexesTable));
ui->indexesTable->setItemDelegateForColumn(0, delegate);
ui->indexesTable->setItemDelegate(pglab_delegate);
ui->indexesTable->setItemDelegateForColumn(0, icon_delegate);
PropertyProxyModel* property_model = new PropertyProxyModel(this);
property_model->setSourceModel(m_tablesModel);
SetTableViewDefault(ui->tablePropertiesTable);
ui->tablePropertiesTable->setModel(property_model);
ui->tablePropertiesTable->setItemDelegate(pglab_delegate);
connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged,
property_model, &PropertyProxyModel::setActiveRow);
//m_namespaceFilterWidget = new NamespaceFilterWidget(this);
//ui->verticalLayoutTableView->addWidget(m_namespaceFilterWidget);

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TablesPage</class>
<widget class="QWidget" name="TablesPage">
@ -13,7 +13,7 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
@ -38,7 +38,7 @@
</widget>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
<number>3</number>
</property>
<widget class="QWidget" name="columnsTab">
<attribute name="title">
@ -90,6 +90,11 @@
<attribute name="title">
<string>Properties</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayoutProperties">
<item>
<widget class="QTableView" name="tablePropertiesTable"/>
</item>
</layout>
</widget>
<widget class="QWidget" name="statisticsTab">
<attribute name="title">

View file

@ -5,13 +5,12 @@
#include "PgNamespace.h"
#include "PgNamespaceContainer.h"
#include "Pgsql_declare.h"
#include "CustomDataRole.h"
#include <QBrush>
TablesTableModel::TablesTableModel(QObject *parent)
: BaseTableModel(parent)
{
}
: QAbstractTableModel(parent)
{}
void TablesTableModel::setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat)
{
@ -166,11 +165,11 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const
v = getTablespaceDisplayString(*m_catalog, t.tablespace);
break;
case OptionsCol:
v = t.options;
//v = t.options;
break;
// case AclCol:
// v = t.acl;
// break;
// case AclCol:
// v = t.acl;
// break;
}
return v;
@ -195,19 +194,9 @@ QString TablesTableModel::formatTableName(const PgClass &cls) const
QVariant TablesTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::ForegroundRole) {
const auto &t = m_tables[index.row()];
auto ns = m_catalog->namespaces()->getByKey(t.relnamespace);
if (ns.isSystemCatalog()) {
switch (index.column()) {
case NameCol:
case NamespaceCol:
return QBrush(Qt::blue);
break;
}
}
}
return BaseTableModel::data(index, role);
if (role == Qt::DisplayRole)
return getData(index);
else if (role == CustomDataTypeRole)
return getType(index.column());
return QVariant();
}

View file

@ -9,8 +9,7 @@
class PgClass;
class PgDatabaseCatalog;
class TablesTableModel: public BaseTableModel
{
class TablesTableModel: public QAbstractTableModel {
public:
enum e_Columns : int {
NameCol, ///< either table, ns.table or table (ns) depending on settings/filters
@ -38,9 +37,6 @@ public:
virtual QVariant data(const QModelIndex &index, int role) const override;
PgClass getTable(int row) const;
Oid getTableOid(int row) const;
protected:
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;
private:
using t_Tables = std::vector<PgClass>;
@ -48,6 +44,8 @@ private:
std::shared_ptr<const PgDatabaseCatalog> m_catalog;
t_Tables m_tables;
Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const;
QString formatTableName(const PgClass &cls) const;
void doSort(int so);
};

View file

@ -73,7 +73,8 @@ SOURCES += main.cpp\
Module.cpp \
EditorGutter.cpp \
CodeEditor.cpp \
PlgPage.cpp
PlgPage.cpp \
PropertyProxyModel.cpp
HEADERS += \
QueryResultModel.h \
@ -119,7 +120,9 @@ HEADERS += \
EditorGutter.h \
CodeEditor.h \
PlgPage.h \
AbstractCommand.h
AbstractCommand.h \
PropertyProxyModel.h \
CustomDataRole.h
FORMS += mainwindow.ui \
ConnectionManagerWindow.ui \

View file

@ -51,8 +51,8 @@ public:
bool ispopulated;
int frozenxid;
int minmxid;
QString acl;
QString options;
std::vector<QString> acl;
std::vector<QString> options;
bool operator==(Oid _oid) const { return oid == _oid; }
bool operator==(const QString &n) const { return name == n; }

View file

@ -3,6 +3,7 @@
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespaceContainer.h"
#include <iterator>
std::string PgClassContainer::getLoadQuery() const
{
@ -21,8 +22,10 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
col >> v.oid >> v.name >> v.relnamespace >> v.type >> v.oftype
>> v.owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est
>> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence
>> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid
>> v.acl >> v.options;
>> v.kind >> v.hasoids >> v.ispopulated >> v.frozenxid >> v.minmxid;
col.getAsArray<QString>(std::back_inserter(v.acl), Pgsql::NullHandling::Ignore);
col.getAsArray<QString>(std::back_inserter(v.options), Pgsql::NullHandling::Ignore);
auto cat = m_catalogue.lock();
auto ns = cat->namespaces()->getByKey(v.relnamespace);

View file

@ -9,6 +9,7 @@
#include "PgDatabaseContainer.h"
#include "PgIndexContainer.h"
#include "PgNamespaceContainer.h"
#include "PgTablespaceContainer.h"
#include "PgTypeContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_oids.h"
@ -36,7 +37,6 @@ QString getRoleDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
QString name = getRoleNameFromOid(cat, oid);
return name;
// return QString("%1 (%2)").arg(name).arg(oid);
}
QString getNamespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
@ -71,7 +71,12 @@ QString getIndexDisplayString(const PgDatabaseCatalog &cat, Oid oid)
QString getTablespaceDisplayString(const PgDatabaseCatalog &cat, Oid oid)
{
// TODO load list and lookup name
return QString("ts %1").arg(oid);
if (oid == 0) {
auto dbname = cat.getDBName();
oid = cat.databases()->getByName(dbname).tablespace;
}
auto ts = cat.tablespaces()->getByKey(oid);
return ts.name;
}
QString getTypeDisplayString(const PgDatabaseCatalog &cat, Oid oid, int32_t typmod)
@ -129,11 +134,14 @@ void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn,
std::function<bool(int, int)> progress_callback)
{
loadInfo(conn);
const int count = 10;
const int count = 11;
int n = 0;
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_namespaces, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_tablespaces, conn);
if (progress_callback && !progress_callback(++n, count))
return;
load2(m_classes, conn); // needs namespaces
@ -172,6 +180,8 @@ void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn)
if (r && r.resultStatus() == PGRES_TUPLES_OK)
if (r.rows() == 1)
m_serverVersionString = r.get(0, 0).asQString();
m_dbName = conn.getDBName();
}
void load(Pgsql::Connection &conn, IPgContainter &pg_cont)
@ -245,6 +255,11 @@ std::shared_ptr<const PgNamespaceContainer> PgDatabaseCatalog::namespaces() cons
return m_namespaces;
}
std::shared_ptr<const PgTablespaceContainer> PgDatabaseCatalog::tablespaces() const
{
return m_tablespaces;
}
std::shared_ptr<const PgTypeContainer> PgDatabaseCatalog::types() const
{
return m_types;

View file

@ -21,6 +21,7 @@ class PgDatabaseContainer;
class PgIndexContainer;
class PgNamespaceContainer;
class PgAmContainer;
class PgTablespaceContainer;
class PgTypeContainer;
class PgDatabaseCatalog: public std::enable_shared_from_this<PgDatabaseCatalog> {
@ -37,6 +38,7 @@ public:
const QString& serverVersionString() const;
int serverVersion() const;
const QString& getDBName() const { return m_dbName; }
std::shared_ptr<const PgAttributeContainer> attributes() const;
std::shared_ptr<const PgAuthIdContainer> authIds() const;
@ -46,10 +48,13 @@ public:
std::shared_ptr<const PgIndexContainer> indexes() const;
std::shared_ptr<const PgAmContainer> ams() const;
std::shared_ptr<const PgNamespaceContainer> namespaces() const;
std::shared_ptr<const PgTablespaceContainer> tablespaces() const;
std::shared_ptr<const PgTypeContainer> types() const;
private:
QString m_serverVersionString;
int m_serverVersion;
QString m_dbName;
std::shared_ptr<PgAttributeContainer> m_attributes;
std::shared_ptr<PgAuthIdContainer> m_authIds;
@ -59,6 +64,7 @@ private:
std::shared_ptr<PgIndexContainer> m_indexes;
std::shared_ptr<PgAmContainer> m_ams;
std::shared_ptr<PgNamespaceContainer> m_namespaces;
std::shared_ptr<PgTablespaceContainer> m_tablespaces;
std::shared_ptr<PgTypeContainer> m_types;
template <typename T>

View file

@ -18,7 +18,6 @@ public:
virtual std::string getLoadQuery() const override;
virtual void load(const Pgsql::Result &res) override;
private:
};

View file

@ -0,0 +1,4 @@
#include "PgTablespace.h"
PgTablespace::PgTablespace()
{}

24
pglablib/PgTablespace.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef PGTABLESPACE_H
#define PGTABLESPACE_H
#include <QString>
#include <libpq-fe.h>
#include <vector>
class PgTablespace {
public:
Oid oid = InvalidOid;
QString name;
Oid owner = InvalidOid;
std::vector<QString> acl;
std::vector<QString> options;
PgTablespace();
bool operator==(Oid _oid) const { return oid == _oid; }
//bool operator==(const QString &n) const { return name == n; }
bool operator<(Oid _oid) const { return oid < _oid; }
bool operator<(const PgTablespace &rhs) const { return oid < rhs.oid; }
};
#endif // PGTABLESPACE_H

View file

@ -0,0 +1,25 @@
#include "PgTablespaceContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "Pgsql_declare.h"
#include "PgDatabaseCatalog.h"
#include <iterator>
std::string PgTablespaceContainer::getLoadQuery() const
{
return
R"__SQL__(
SELECT oid, spcname, spcowner, spcacl, spcoptions
FROM pg_tablespace
)__SQL__";
}
PgTablespace PgTablespaceContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
PgTablespace v;
col >> v.oid >> v.name >> v.owner;
col.getAsArray<QString>(std::back_inserter(v.acl), Pgsql::NullHandling::Ignore);
col.getAsArray<QString>(std::back_inserter(v.options), Pgsql::NullHandling::Ignore);
return v;
}

View file

@ -0,0 +1,24 @@
#ifndef PGTABLESPACECONTAINER_H
#define PGTABLESPACECONTAINER_H
#include "PgContainer.h"
#include "PgTablespace.h"
namespace Pgsql {
class Result;
}
class PgTablespaceContainer: public PgContainer<PgTablespace> {
public:
using PgContainer<PgTablespace>::PgContainer;
virtual std::string getLoadQuery() const override;
protected:
PgTablespace loadElem(const Pgsql::Row &row) override;
private:
};
#endif // PGTABLESPACECONTAINER_H

View file

@ -55,7 +55,9 @@ SOURCES += \
QueryGenerator.cpp \
PgAm.cpp \
PgAmContainer.cpp \
PgObject.cpp
PgObject.cpp \
PgTablespace.cpp \
PgTablespaceContainer.cpp
HEADERS += \
Pglablib.h \
@ -88,7 +90,9 @@ HEADERS += \
QueryGenerator.h \
PgAm.h \
PgAmContainer.h \
PgObject.h
PgObject.h \
PgTablespace.h \
PgTablespaceContainer.h
unix {
target.path = /usr/lib

View file

@ -263,3 +263,8 @@ void Connection::notifyReceiveFunc(void *arg, const PGresult *result)
Connection *c = reinterpret_cast<Connection *>(arg);
c->notifyReceiver(result);
}
QString Connection::getDBName() const
{
return QString::fromUtf8(PQdb(conn));
}

View file

@ -124,6 +124,8 @@ namespace Pgsql {
QString escapeLiteral(const QString &literal);
std::string escapeIdentifier(const std::string_view &ident);
QString escapeIdentifier(const QString &ident);
QString getDBName() const;
private:
PGconn *conn = nullptr;
std::function<void(const PGresult *)> notifyReceiver;

View file

@ -177,6 +177,13 @@ namespace Pgsql {
static Oid elem() { return timestamptz_oid; }
static Oid array() { return timestamptz_array_oid; }
};
template <>
class OidFor<QString> {
public:
static Oid elem() { return text_oid; }
static Oid array() { return text_array_oid; }
};
// template <>
// class OidFor<> {
// public: