From 4c175d8c2c8d14b713ee4659ab558aa26f16c5b1 Mon Sep 17 00:00:00 2001 From: eelke Date: Thu, 1 Apr 2021 14:58:42 +0200 Subject: [PATCH] Added page with the types (no details yet) --- pglab/BaseTableModel.cpp | 2 +- pglab/CatalogInspector.cpp | 6 ++ pglab/CatalogInspector.h | 2 + pglab/ColumnPage.cpp | 2 +- pglab/PropertyProxyModel.cpp | 2 +- pglab/RolesTableModel.cpp | 1 - pglab/TypeModel.cpp | 101 ++++++++++++++++++ pglab/TypeModel.h | 40 +++++++ pglab/pglab.pro | 6 +- pglab/widgets/CatalogTypesPage.cpp | 78 ++++++++++++++ pglab/widgets/CatalogTypesPage.h | 34 ++++++ pglablib/catalog/PgType.cpp | 5 +- pglablib/model/TypeSelectionItemModel.cpp | 85 ++++++++++++++- pglablib/model/TypeSelectionItemModel.h | 11 +- pglablib/util.cpp | 43 ++++++++ .../pglabtests/tst_ConvertLangToSqlString.cpp | 11 ++ 16 files changed, 418 insertions(+), 11 deletions(-) create mode 100644 pglab/TypeModel.cpp create mode 100644 pglab/TypeModel.h create mode 100644 pglab/widgets/CatalogTypesPage.cpp create mode 100644 pglab/widgets/CatalogTypesPage.h diff --git a/pglab/BaseTableModel.cpp b/pglab/BaseTableModel.cpp index 536673f..8bdf333 100644 --- a/pglab/BaseTableModel.cpp +++ b/pglab/BaseTableModel.cpp @@ -22,7 +22,7 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const return v; } -QVariant BaseTableModel::getDataMeaning(const QModelIndex &index) const +QVariant BaseTableModel::getDataMeaning(const QModelIndex &) const { return static_cast(DataMeaningNormal); } diff --git a/pglab/CatalogInspector.cpp b/pglab/CatalogInspector.cpp index 06b2320..a9bc84d 100644 --- a/pglab/CatalogInspector.cpp +++ b/pglab/CatalogInspector.cpp @@ -5,6 +5,7 @@ #include "widgets/CatalogNamespacePage.h" #include "widgets/CatalogSequencesPage.h" #include "widgets/CatalogTablesPage.h" +#include "widgets/CatalogTypesPage.h" #include #include @@ -20,6 +21,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr open_database, m_tablesPage = new CatalogTablesPage(this); m_functionsPage = new CatalogFunctionsPage(this); m_sequencesPage = new CatalogSequencesPage(this); + m_typesPage = new CatalogTypesPage(this); auto layout = new QVBoxLayout(this); setLayout(layout); @@ -28,6 +30,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr open_database, m_tabWidget->addTab(m_tablesPage, ""); m_tabWidget->addTab(m_functionsPage, ""); m_tabWidget->addTab(m_sequencesPage, ""); + m_tabWidget->addTab(m_typesPage, ""); setCatalog(open_database->catalog()); retranslateUi(false); @@ -45,6 +48,8 @@ void CatalogInspector::retranslateUi(bool all) QApplication::translate("CatalogInspector", "Functions", nullptr)); m_tabWidget->setTabText(m_tabWidget->indexOf(m_sequencesPage), QApplication::translate("CatalogInspector", "Sequences", nullptr)); + m_tabWidget->setTabText(m_tabWidget->indexOf(m_typesPage), + QApplication::translate("CatalogInspector", "Types", nullptr)); } CatalogInspector::~CatalogInspector() @@ -58,6 +63,7 @@ void CatalogInspector::setCatalog(std::shared_ptr cat) m_tablesPage->setCatalog(cat); m_functionsPage->setCatalog(cat); m_sequencesPage->setCatalog(cat); + m_typesPage->setCatalog(cat); } void CatalogInspector::setNamespaceFilter(NamespaceFilter filter) diff --git a/pglab/CatalogInspector.h b/pglab/CatalogInspector.h index 499cf50..ce4b97e 100644 --- a/pglab/CatalogInspector.h +++ b/pglab/CatalogInspector.h @@ -9,6 +9,7 @@ class CatalogFunctionsPage; class CatalogSequencesPage; class CatalogTablesPage; class CatalogNamespacePage; +class CatalogTypesPage; class OpenDatabase; class PgDatabaseCatalog; class QTabWidget; @@ -29,6 +30,7 @@ private: CatalogTablesPage *m_tablesPage = nullptr; CatalogFunctionsPage *m_functionsPage = nullptr; CatalogSequencesPage *m_sequencesPage = nullptr; + CatalogTypesPage *m_typesPage = nullptr; std::shared_ptr m_catalog; void retranslateUi(bool all = true); diff --git a/pglab/ColumnPage.cpp b/pglab/ColumnPage.cpp index 9c5e2e3..6921a45 100644 --- a/pglab/ColumnPage.cpp +++ b/pglab/ColumnPage.cpp @@ -29,7 +29,7 @@ ColumnPage::ColumnPage(QWidget *parent) m_tableView->setItemDelegateForColumn(ColumnTableModel::TypeCol, new QStyledItemDelegate(this)); m_tableView->setModel(m_sortFilterProxy); m_tableView->setSortingEnabled(true); - m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); +// m_tableView->setSelectionBehavior(QAbstractItemView::SelectRows); m_sortFilterProxy->sort(ColumnTableModel::AttnumCol, Qt::AscendingOrder); connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged, diff --git a/pglab/PropertyProxyModel.cpp b/pglab/PropertyProxyModel.cpp index f9c58a3..58d23fd 100644 --- a/pglab/PropertyProxyModel.cpp +++ b/pglab/PropertyProxyModel.cpp @@ -104,7 +104,7 @@ void PropertyProxyModel::setActiveRow(const QModelIndex &row) QVector() << Qt::DisplayRole); } -Qt::ItemFlags PropertyProxyModel::flags(const QModelIndex &index) const +Qt::ItemFlags PropertyProxyModel::flags(const QModelIndex &) const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } diff --git a/pglab/RolesTableModel.cpp b/pglab/RolesTableModel.cpp index 4e0ac65..005d96a 100644 --- a/pglab/RolesTableModel.cpp +++ b/pglab/RolesTableModel.cpp @@ -141,7 +141,6 @@ QVariant RolesTableModel::getData(const QModelIndex &index) const v = authid.bypassRls; break; case ConnlimitCol: - // todo lookup tablespace name v = authid.connLimit; break; case ValidUntilCol: diff --git a/pglab/TypeModel.cpp b/pglab/TypeModel.cpp new file mode 100644 index 0000000..476a5e3 --- /dev/null +++ b/pglab/TypeModel.cpp @@ -0,0 +1,101 @@ +#include "TypeModel.h" +#include "catalog/PgDatabaseCatalog.h" +#include "catalog/PgNamespace.h" +#include "catalog/PgTypeContainer.h" + +TypeModel::TypeModel(QObject * parent) + : QAbstractTableModel(parent) +{ +} + +QVariant TypeModel::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 SchemaCol: return tr("Schema"); + case OwnerCol: return tr("Owner"); + } + } + } + return {}; +} + +void TypeModel::setCatalog(std::shared_ptr cat) +{ + if (cat != m_catalog) { + m_catalog = cat; + refreshConnection = connect(m_catalog.get(), &PgDatabaseCatalog::refreshed, + this, &TypeModel::refresh); + } + refresh(); +} + +//void TypeModel::setNamespaceFilter(NamespaceFilter filter) +//{ +// m_namespaceFilter = filter; +// refresh(); +//} + +void TypeModel::refresh() +{ + if (!m_catalog) + return; + + beginResetModel(); + + auto && typs = m_catalog->types(); + m_types.clear(); + for (auto&& s : *typs) { +// bool add = false; +// switch (m_namespaceFilter) { +// case NamespaceFilter::User: +// add = !s.ns().isSystemCatalog(); +// break; +// case NamespaceFilter::PgCatalog: +// add = s.ns().objectName() == "pg_catalog"; +// break; +// case NamespaceFilter::InformationSchema: +// add = s.ns().objectName() == "information_schema"; +// break; +// } +// if (add) + m_types.push_back(s); + } + + endResetModel(); +} + +PgType TypeModel::typ(int row) const +{ + return m_types.at(static_cast(row)); +} + +int TypeModel::rowCount(const QModelIndex &) const +{ + return static_cast(m_types.size()); +} + +int TypeModel::columnCount(const QModelIndex &) const +{ + return colCount; +} + +QVariant TypeModel::data(const QModelIndex &index, int role) const +{ + if (m_types.empty()) + return {}; + + int row = index.row(); + auto && seq = m_types.at(static_cast(row)); + if (role == Qt::DisplayRole) { + switch (index.column()) { + case NameCol: return seq.objectName(); + case SchemaCol: return seq.nsName(); + case OwnerCol: return seq.ownerName(); + } + } + + return {}; +} diff --git a/pglab/TypeModel.h b/pglab/TypeModel.h new file mode 100644 index 0000000..401ff8e --- /dev/null +++ b/pglab/TypeModel.h @@ -0,0 +1,40 @@ +#ifndef TYPEMODEL_H +#define TYPEMODEL_H + +#include +#include "catalog/PgType.h" + +class TypeModel : public QAbstractTableModel +{ + Q_OBJECT +public: + enum e_Columns : int { + NameCol, + SchemaCol, + OwnerCol, + + colCount + }; + + TypeModel(QObject * parent = nullptr); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void setCatalog(std::shared_ptr cat); + //void setNamespaceFilter(NamespaceFilter filter); + + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant data(const QModelIndex &index, int role) const override; + + PgType typ(int row) const; + +private: + std::shared_ptr m_catalog; + std::vector m_types; + //NamespaceFilter m_namespaceFilter = NamespaceFilter::User; + QMetaObject::Connection refreshConnection; + + void refresh(); +}; + +#endif diff --git a/pglab/pglab.pro b/pglab/pglab.pro index bf4168f..e201e68 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -87,7 +87,8 @@ PropertyProxyModel.cpp \ widgets/CatalogConstraintPage.cpp \ widgets/CatalogTablesPage.cpp \ widgets/CatalogFunctionsPage.cpp \ - widgets/CatalogSequencesPage.cpp + widgets/CatalogSequencesPage.cpp \ + widgets/CatalogTypesPage.cpp HEADERS += \ ConnectionConfigurationWidget.h \ @@ -160,7 +161,8 @@ CustomDataRole.h \ widgets/CatalogTablesPage.h \ widgets/CatalogFunctionsPage.h \ widgets/CatalogSequencesPage.h \ - NamespaceFilter.h + NamespaceFilter.h \ + widgets/CatalogTypesPage.h FORMS += \ ConnectionManagerWindow.ui \ diff --git a/pglab/widgets/CatalogTypesPage.cpp b/pglab/widgets/CatalogTypesPage.cpp new file mode 100644 index 0000000..a924e5f --- /dev/null +++ b/pglab/widgets/CatalogTypesPage.cpp @@ -0,0 +1,78 @@ +#include "CatalogTypesPage.h" +#include "ResultTableModelUtil.h" +#include "CustomFilterSortModel.h" +#include "CustomDataRole.h" +#include "PgLabItemDelegate.h" +#include "catalog/PgType.h" +#include "model/TypeSelectionItemModel.h" +#include "SqlCodePreview.h" +#include "PgLabTableView.h" +#include + +CatalogTypesPage::CatalogTypesPage(QWidget *parent) + : QSplitter(Qt::Horizontal, parent) +{ + m_typeTable = new PgLabTableView(this); + m_definitionView = new SqlCodePreview(this); + + // build widget tree + // add top level widgets to splitter + addWidget(m_typeTable); + addWidget(m_definitionView); + + m_model = new TypeModel(this); + m_sortFilterProxy = new CustomFilterSortModel(this); + m_sortFilterProxy->setSourceModel(m_model); + m_typeTable->setModel(m_sortFilterProxy); + m_typeTable->setSortingEnabled(true); + m_typeTable->setSelectionBehavior(QAbstractItemView::SelectRows); + m_typeTable->setSelectionMode(QAbstractItemView::SingleSelection); + + connect(m_typeTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, + &CatalogTypesPage::typeTable_currentRowChanged); + connect(m_model, &TypeModel::modelReset, + [this] () { selectedTypeChanged({}); }); + + retranslateUi(); + +} + +void CatalogTypesPage::retranslateUi() +{ +} + +void CatalogTypesPage::setCatalog(std::shared_ptr cat) +{ + m_catalog = cat; + m_model->setTypeList(cat->types()); + m_typeTable->resizeColumnsToContents(); +} + +void CatalogTypesPage::typeTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (current.row() != previous.row()) { + if (current.isValid()) { + auto source_index = m_sortFilterProxy->mapToSource(current); + auto proc = m_model->typ(source_index.row()); + selectedTypeChanged(proc); + } + else + selectedTypeChanged({}); + } +} + +void CatalogTypesPage::selectedTypeChanged(const std::optional &typ) +{ + updateSqlTab(typ); +} + +void CatalogTypesPage::updateSqlTab(const std::optional &typ) +{ + if (!typ.has_value()) { + m_definitionView->clear(); + return; + } + QString create_sql = typ->createSql(); + + m_definitionView->setPlainText(create_sql); +} diff --git a/pglab/widgets/CatalogTypesPage.h b/pglab/widgets/CatalogTypesPage.h new file mode 100644 index 0000000..0390cff --- /dev/null +++ b/pglab/widgets/CatalogTypesPage.h @@ -0,0 +1,34 @@ +#pragma once + +#include + +class PgLabTableView; +class PgDatabaseCatalog; +class TypeModel; +class CustomFilterSortModel; +class SqlCodePreview; +class PgType; + + +class CatalogTypesPage : public QSplitter +{ + Q_OBJECT +public: + CatalogTypesPage(QWidget *parent = nullptr); + void setCatalog(std::shared_ptr cat); +public slots: + + void typeTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); +private: + PgLabTableView *m_typeTable = nullptr; + //QTabWidget *m_detailTabs = nullptr; + SqlCodePreview *m_definitionView = nullptr; + TypeModel *m_model = nullptr; + CustomFilterSortModel *m_sortFilterProxy = nullptr; + std::shared_ptr m_catalog; + + void retranslateUi(); + void selectedTypeChanged(const std::optional &seq); + void updateSqlTab(const std::optional &seq); +}; + diff --git a/pglablib/catalog/PgType.cpp b/pglablib/catalog/PgType.cpp index d5dd466..ee2bf8d 100644 --- a/pglablib/catalog/PgType.cpp +++ b/pglablib/catalog/PgType.cpp @@ -12,7 +12,10 @@ void operator<<(TypCategory &s, const Pgsql::Value &v) case 'B': s = TypCategory::Boolean; break; - case 'D': + case 'C': + s = TypCategory::Composite; + break; + case 'D': s = TypCategory::DateTime; break; case 'E': diff --git a/pglablib/model/TypeSelectionItemModel.cpp b/pglablib/model/TypeSelectionItemModel.cpp index 2a85da5..651f6c4 100644 --- a/pglablib/model/TypeSelectionItemModel.cpp +++ b/pglablib/model/TypeSelectionItemModel.cpp @@ -1,4 +1,5 @@ #include "TypeSelectionItemModel.h" +//#include "CustomDataRole.h" #include "catalog/PgTypeContainer.h" #include @@ -56,7 +57,7 @@ void TypeSelectionItemModel::setTypeList(std::shared_ptr std::sort(m_types.begin(), m_types.end()); } //emit dataChanged(this->createIndex(0, 0), this->createIndex(types->count(), 0), QVector() << Qt::DisplayRole); - endResetModel(); + endResetModel(); } // ---------------- @@ -82,6 +83,9 @@ int TypeModel::columnCount(const QModelIndex &/*parent*/) const QVariant TypeModel::data(const QModelIndex &index, int role) const { +// if (role == CustomDataTypeRole) +// return getType(index.column()); + if (index.isValid()) { int row = index.row(); int column = index.column(); @@ -91,15 +95,90 @@ QVariant TypeModel::data(const QModelIndex &index, int role) const switch (column) { case OidCol: return elem.oid(); case NameCol: return elem.objectName(); + case NamespaceCol: return elem.nsName(); + case OwnerCol: return elem.ownerName(); + case CategoryCol: return TypCategoryString(elem.category); } } } - return QVariant(); + return QVariant(); +} + +QString TypeModel::TypCategoryString(TypCategory tc) +{ + switch (tc) { + case TypCategory::Array: + return tr("array"); + case TypCategory::Boolean: + return tr("boolean"); + case TypCategory::Composite: + return tr("composite"); + case TypCategory::DateTime: + return tr("datetime"); + case TypCategory::Enum: + return tr("enum"); + case TypCategory::Geometric: + return tr("geometric"); + case TypCategory::NetworkAddress: + return tr("networkaddress"); + case TypCategory::Numeric: + return tr("numeric"); + case TypCategory::Pseudo: + return tr("pseude"); + case TypCategory::Range: + return tr("range"); + case TypCategory::String: + return tr("string"); + case TypCategory::Timespan: + return tr("timespan"); + case TypCategory::UserDefined: + return tr("user"); + case TypCategory::BitString: + return tr("bitstring"); + case TypCategory::Unknown: + return tr("unknown"); + } + return "?"; +} + +Oid TypeModel::getType(int column) const +{ + switch (column) { + case OidCol: + return Pgsql::oid_oid; + case NameCol: + case NamespaceCol: + case OwnerCol: + case CategoryCol: + return Pgsql::varchar_oid; + } + return InvalidOid; } void TypeModel::setTypeList(std::shared_ptr types) { beginResetModel(); m_types = types; - endResetModel(); + endResetModel(); +} + +PgType TypeModel::typ(int row) const +{ + return m_types->getByIdx(row); +} + +QVariant TypeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal) { + if (role == Qt::DisplayRole) { + switch (section) { + case OidCol: return tr("Oid"); + case NameCol: return tr("Name"); + case NamespaceCol: return tr("Schema"); + case OwnerCol: return tr("Owner"); + case CategoryCol: return tr("Category"); + } + } + } + return {}; } diff --git a/pglablib/model/TypeSelectionItemModel.h b/pglablib/model/TypeSelectionItemModel.h index c5e71fb..4aa024f 100644 --- a/pglablib/model/TypeSelectionItemModel.h +++ b/pglablib/model/TypeSelectionItemModel.h @@ -4,6 +4,7 @@ #include #include #include +#include "catalog/PgType.h" class PgTypeContainer; @@ -28,19 +29,27 @@ public: enum e_Columns : int { OidCol, NameCol, // + NamespaceCol, + OwnerCol, + CategoryCol, colCount }; explicit TypeModel(QObject *parent = 0); void setTypeList(std::shared_ptr types); + PgType typ(int row) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; private: std::shared_ptr m_types; + + static QString TypCategoryString(TypCategory tc); + Oid getType(int column) const; }; diff --git a/pglablib/util.cpp b/pglablib/util.cpp index 3871cdd..788e2c0 100644 --- a/pglablib/util.cpp +++ b/pglablib/util.cpp @@ -211,6 +211,48 @@ QString ConvertToMultiLineRawCppString(const QString &in) return out; } +namespace { + + class Token { + public: + enum Type { + Code, + StringLiteral, + SingleLineComment, + MultiLineComment + }; + + const Type type; + /// Depends on type + /// Code: literal copy of the matched code + /// StringLiteral: the contents of the string literal, escapes have been unescaped + /// *Comment, the text in the comment + const QString data; + + Token(Type type, QString data) + : type(type) + , data(data) + {} + }; + + /// Tokenizer to get SQL out of a piece of programming language code + /// + /// It works by ignoring most input and only get's triggered by string literals + /// and comments. It does return tokens for the code in between just so + class ProgLangTokenizer { + public: + ProgLangTokenizer(const QString &in) + : input(in) + {} + + private: + const QString input; + int position = 0; + + }; + +} + QString ConvertLangToSqlString(const QString &in) { // Assume mostly C++ for now but allow some other things like @@ -232,6 +274,7 @@ QString ConvertLangToSqlString(const QString &in) WHITESPACE, PREFIX, IN_STRING, + SingleLineComment, END, ERROR } state = WHITESPACE; diff --git a/tests/pglabtests/tst_ConvertLangToSqlString.cpp b/tests/pglabtests/tst_ConvertLangToSqlString.cpp index 8310a7c..fccc372 100644 --- a/tests/pglabtests/tst_ConvertLangToSqlString.cpp +++ b/tests/pglabtests/tst_ConvertLangToSqlString.cpp @@ -60,3 +60,14 @@ TEST(ConvertLangToSqlString, testSemiColon) auto output = ConvertLangToSqlString(in); ASSERT_EQ(output, expected); } + +TEST(ConvertLangToSqlString, testComment) +{ + QString in(R"__( "SELECT * " // comment + "FROM t"; )__"); + QString expected(R"__(SELECT * +FROM t)__"); + + auto output = ConvertLangToSqlString(in); + ASSERT_EQ(output, expected); +}