From 2a75e861024a3f7d636508e61a60376224566449 Mon Sep 17 00:00:00 2001 From: eelke Date: Sun, 7 Oct 2018 19:40:06 +0200 Subject: [PATCH] Added listing of triggers for selected table (not completely finished). Used slightly different approach. This tab is fully build in source code using subclasses to adjust behaviour of widgets for reuse in the other tabs. Uses custom proxy model for filtering triggers for correct table and supporting out of the box sorting by QTableView. SqlCodePreview: QPlainTextEditor which sql highlighter and in readonly mode but allows copy. --- common.pri | 2 +- pglab/CustomDataRole.h | 2 + pglab/CustomFilterSortModel.cpp | 27 +++++++ pglab/CustomFilterSortModel.h | 20 +++++ pglab/SqlCodePreview.cpp | 27 +++++++ pglab/SqlCodePreview.h | 30 ++++++++ pglab/SqlSyntaxHighlighter.cpp | 3 +- pglab/TablesPage.cpp | 70 +++++++++++++----- pglab/TablesPage.h | 5 ++ pglab/TablesPage.ui | 17 +---- pglab/TriggerPage.cpp | 71 ++++++++++++++++++ pglab/TriggerPage.h | 41 +++++++++++ pglab/TriggerTableModel.cpp | 100 +++++++++++++++++++++++++ pglab/TriggerTableModel.h | 75 +++++++++++++++++++ pglab/pglab.pro | 13 +++- pglablib/PgDatabaseCatalog.cpp | 13 +++- pglablib/PgDatabaseCatalog.h | 26 ++++++- pglablib/PgTrigger.cpp | 126 +++++++++++++++++++++++++++++++- pglablib/PgTrigger.h | 63 ++++++++++++---- pglablib/PgTriggerContainer.cpp | 22 ++++-- pglablib/PgTriggerContainer.h | 3 + pgsql/Pgsql_Value.cpp | 7 +- pgsql/Pgsql_Value.h | 1 + 23 files changed, 697 insertions(+), 67 deletions(-) create mode 100644 pglab/CustomFilterSortModel.cpp create mode 100644 pglab/CustomFilterSortModel.h create mode 100644 pglab/SqlCodePreview.cpp create mode 100644 pglab/SqlCodePreview.h create mode 100644 pglab/TriggerPage.cpp create mode 100644 pglab/TriggerPage.h create mode 100644 pglab/TriggerTableModel.cpp create mode 100644 pglab/TriggerTableModel.h diff --git a/common.pri b/common.pri index 39e9269..aa6728e 100644 --- a/common.pri +++ b/common.pri @@ -9,4 +9,4 @@ QMAKE_CXXFLAGS += /std:c++17 # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS -DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX +DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501 diff --git a/pglab/CustomDataRole.h b/pglab/CustomDataRole.h index dccd92e..590f08a 100644 --- a/pglab/CustomDataRole.h +++ b/pglab/CustomDataRole.h @@ -5,6 +5,8 @@ enum CustomDataRole { CustomDataTypeRole = Qt::UserRole, + // Add other enum before this one is we might want to have multiple hidden values + FirstHiddenValue, }; #endif // CUSTOMDATAROLE_H diff --git a/pglab/CustomFilterSortModel.cpp b/pglab/CustomFilterSortModel.cpp new file mode 100644 index 0000000..c3ec10b --- /dev/null +++ b/pglab/CustomFilterSortModel.cpp @@ -0,0 +1,27 @@ +#include "CustomFilterSortModel.h" + +CustomFilterSortModel::CustomFilterSortModel(QObject *parent) + : QSortFilterProxyModel(parent) +{ +} + + +void CustomFilterSortModel::setOidFilterTable(Oid tbl_oid, int role) +{ + m_filterOid = tbl_oid; + m_filterRole = role; + invalidateFilter(); +} + + +bool CustomFilterSortModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const +{ + bool result = true; + if (m_filterOid != InvalidOid) { + auto&& source = sourceModel(); + QModelIndex index0 = source->index(sourceRow, 0, sourceParent); + auto&& v = source->data(index0, m_filterRole); + result = v.toUInt() == m_filterOid; + } + return result && QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); +} diff --git a/pglab/CustomFilterSortModel.h b/pglab/CustomFilterSortModel.h new file mode 100644 index 0000000..7db0d08 --- /dev/null +++ b/pglab/CustomFilterSortModel.h @@ -0,0 +1,20 @@ +#ifndef TRIGGERFILTERSORTMODEL_H +#define TRIGGERFILTERSORTMODEL_H + +#include +#include "Pgsql_oids.h" + +class CustomFilterSortModel : public QSortFilterProxyModel { +public: + CustomFilterSortModel(QObject *parent = nullptr); + + void setOidFilterTable(Oid tbl_oid, int role); + +protected: + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; +private: + Oid m_filterOid = InvalidOid; + int m_filterRole = Qt::DisplayRole; +}; + +#endif // TRIGGERFILTERSORTMODEL_H diff --git a/pglab/SqlCodePreview.cpp b/pglab/SqlCodePreview.cpp new file mode 100644 index 0000000..3a65be1 --- /dev/null +++ b/pglab/SqlCodePreview.cpp @@ -0,0 +1,27 @@ +#include "SqlCodePreview.h" +#include "UserConfiguration.h" +#include "SqlSyntaxHighlighter.h" + +SqlCodePreview::SqlCodePreview(QWidget *parent) + : QPlainTextEdit(parent) +{ + auto&& config = UserConfiguration::instance(); + setFont(config->codeFont()); + setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + m_highlighter = new SqlSyntaxHighlighter(document()); +} + + +void SqlCodePreview::setCatalog(std::shared_ptr catalog) +{ + connect(catalog.get(), &PgDatabaseCatalog::refreshed, this, &SqlCodePreview::catalogRefresh); + catalogRefresh(catalog.get(), PgDatabaseCatalog::All); +} + + +void SqlCodePreview::catalogRefresh(const PgDatabaseCatalog *catalog, PgDatabaseCatalog::RefreshFlags flags) +{ + if (flags & PgDatabaseCatalog::Types) { + m_highlighter->setTypes(*catalog->types()); + } +} diff --git a/pglab/SqlCodePreview.h b/pglab/SqlCodePreview.h new file mode 100644 index 0000000..dd0580f --- /dev/null +++ b/pglab/SqlCodePreview.h @@ -0,0 +1,30 @@ +#ifndef SQLCODEPREVIEW_H +#define SQLCODEPREVIEW_H + +#include "PgDatabaseCatalog.h" +#include +#include + +class SqlSyntaxHighlighter; +class PgDatabaseCatalog; + +class SqlCodePreview : public QPlainTextEdit { + Q_OBJECT +public: + SqlCodePreview(QWidget *parent = nullptr); + + /** Sets the database catalog that the syntax highlighter is to use. + */ + void setCatalog(std::shared_ptr catalog); + + + SqlSyntaxHighlighter *highlighter() { return m_highlighter; } +private: + SqlSyntaxHighlighter *m_highlighter = nullptr; + std::shared_ptr m_catalog; + +private slots: + void catalogRefresh(const PgDatabaseCatalog *catalog, PgDatabaseCatalog::RefreshFlags flags); +}; + +#endif // SQLCODEPREVIEW_H diff --git a/pglab/SqlSyntaxHighlighter.cpp b/pglab/SqlSyntaxHighlighter.cpp index 128b7a6..73ecbb1 100644 --- a/pglab/SqlSyntaxHighlighter.cpp +++ b/pglab/SqlSyntaxHighlighter.cpp @@ -31,9 +31,10 @@ SqlSyntaxHighlighter::~SqlSyntaxHighlighter() void SqlSyntaxHighlighter::setTypes(const PgTypeContainer& types) { m_typeNames.clear(); - for (auto &e : types) { + for (auto&& e : types) { m_typeNames.insert(e.name); } + rehighlight(); } void SqlSyntaxHighlighter::highlightBlock(const QString &text) diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index 9f33e57..97bfb23 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -3,19 +3,21 @@ #include "PgAttribute.h" #include "PgDatabaseCatalog.h" -#include "TablesTableModel.h" -#include "ResultTableModelUtil.h" #include "ColumnTableModel.h" #include "ConstraintModel.h" -#include "PropertyProxyModel.h" #include "IconColumnDelegate.h" #include "IndexModel.h" -#include "SqlFormattingUtils.h" -#include "SqlSyntaxHighlighter.h" -#include -#include #include "MainWindow.h" #include "PgLabItemDelegate.h" +#include "PropertyProxyModel.h" +#include "ResultTableModelUtil.h" +#include "SqlFormattingUtils.h" +#include "SqlSyntaxHighlighter.h" +#include "TablesTableModel.h" +#include "TriggerPage.h" +#include "UserConfiguration.h" +#include +#include TablesPage::TablesPage(MainWindow *parent) : QWidget(parent) @@ -34,42 +36,43 @@ TablesPage::TablesPage(MainWindow *parent) ui->tableListTable->setSortingEnabled(true); ui->tableListTable->sortByColumn(0, Qt::AscendingOrder); + // Columns SetTableViewDefault(ui->columnsTable); m_columnsModel = new ColumnTableModel(this); ui->columnsTable->setModel(m_columnsModel); + // Constraints SetTableViewDefault(ui->constraintsTable); m_constraintModel = new ConstraintModel(this); ui->constraintsTable->setModel(m_constraintModel); ui->constraintsTable->setItemDelegateForColumn(0, icon_delegate); - QFont font; - font.setFamily("Source Code Pro"); - font.setFixedPitch(true); - font.setPointSize(10); - ui->constraintSqlEdit->setFont(font); - ui->indexSqlEdit->setFont(font); - + // Indexes SetTableViewDefault(ui->indexesTable); m_indexModel = new IndexModel(this); ui->indexesTable->setModel(m_indexModel); ui->indexesTable->setItemDelegate(pglab_delegate); ui->indexesTable->setItemDelegateForColumn(0, icon_delegate); + // Properties PropertyProxyModel* property_model = new PropertyProxyModel(this); property_model->setSourceModel(m_tablesModel); SetTableViewDefault(ui->tablePropertiesTable); ui->tablePropertiesTable->setModel(property_model); ui->tablePropertiesTable->setItemDelegate(pglab_delegate); + + // Set code editor fonts + QFont code_font = UserConfiguration::instance()->codeFont(); + ui->constraintSqlEdit->setFont(code_font); + ui->indexSqlEdit->setFont(code_font); + + + // Connect signals + // --------------- + // Table selection connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, property_model, &PropertyProxyModel::setActiveRow); - - - //m_namespaceFilterWidget = new NamespaceFilterWidget(this); - //ui->verticalLayoutTableView->addWidget(m_namespaceFilterWidget); - - // Table selection connect(ui->tableListTable->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &TablesPage::tableListTable_currentRowChanged); @@ -88,6 +91,29 @@ TablesPage::TablesPage(MainWindow *parent) connect(ui->indexesTable->model(), &QAbstractItemModel::modelReset, this, &TablesPage::indexesTable_modelReset); + // Non designer based code + m_triggerPage = new TriggerPage(this); + m_triggerTab = addDetailTab(m_triggerPage, tr("Triggers")); + retranslateUi(false); +} + + +void TablesPage::retranslateUi(bool all) +{ + if (all) + ui->retranslateUi(this); + + ui->twDetails->setTabText(ui->twDetails->indexOf(m_triggerTab), QApplication::translate("TablesPage", "Triggers", nullptr)); +} + + +QWidget* TablesPage::addDetailTab(QWidget *contents, QString caption) +{ + auto tab = new QWidget(); + auto verticalLayout = new QVBoxLayout(tab); + verticalLayout->addWidget(contents); + ui->twDetails->addTab(tab, caption); + return tab; } TablesPage::~TablesPage() @@ -100,6 +126,8 @@ void TablesPage::setCatalog(std::shared_ptr cat) m_catalog = cat; m_tablesModel->setCatalog(cat); ui->tableListTable->resizeColumnsToContents(); + + m_triggerPage->setCatalog(cat); // m_namespaceFilterWidget->init(cat->namespaces()); auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document()); @@ -121,6 +149,8 @@ void TablesPage::tableListTable_currentRowChanged(const QModelIndex ¤t, co m_indexModel->setData(m_catalog, table); ui->indexesTable->resizeColumnsToContents(); + + m_triggerPage->setFilter(table); } } diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index 6098399..81c437a 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -16,6 +16,7 @@ class PgDatabaseCatalog; class NamespaceFilterWidget; class IndexModel; class MainWindow; +class TriggerPage; class TablesPage : public QWidget { @@ -29,6 +30,8 @@ public: private: Ui::TablesPage *ui; MainWindow *m_window; + QWidget *m_triggerTab; + TriggerPage *m_triggerPage; std::shared_ptr m_catalog; TablesTableModel* m_tablesModel = nullptr; ColumnTableModel* m_columnsModel = nullptr; @@ -36,6 +39,8 @@ private: IndexModel* m_indexModel = nullptr; //NamespaceFilterWidget* m_namespaceFilterWidget; + void retranslateUi(bool all = true); + QWidget* addDetailTab(QWidget *contents, QString caption); private slots: void tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); diff --git a/pglab/TablesPage.ui b/pglab/TablesPage.ui index 0df84f6..93c8b26 100644 --- a/pglab/TablesPage.ui +++ b/pglab/TablesPage.ui @@ -36,7 +36,7 @@ - + 3 @@ -96,21 +96,6 @@ - - - Statistics - - - - - Dependencies - - - - - Dependents - - diff --git a/pglab/TriggerPage.cpp b/pglab/TriggerPage.cpp new file mode 100644 index 0000000..3eecfff --- /dev/null +++ b/pglab/TriggerPage.cpp @@ -0,0 +1,71 @@ +#include "TriggerPage.h" +#include "ResultTableModelUtil.h" +#include "UserConfiguration.h" +#include +#include "PgClass.h" +#include "SqlCodePreview.h" +#include "TriggerTableModel.h" +#include "CustomFilterSortModel.h" +#include "CustomDataRole.h" +#include "PgLabItemDelegate.h" +#include +#include + +TriggerPage::TriggerPage(QWidget *parent) + : QSplitter(Qt::Vertical, parent) +{ + m_tableView = new QTableView(this); + m_definitionView = new SqlCodePreview(this); + addWidget(m_tableView); + addWidget(m_definitionView); + + SetTableViewDefault(m_tableView); + + QFont code_font = UserConfiguration::instance()->codeFont(); + m_definitionView->setFont(code_font); + + m_model = new TriggerTableModel(this); + m_sortFilterProxy = new CustomFilterSortModel(this); + m_sortFilterProxy->setSourceModel(m_model); + m_tableView->setModel(m_sortFilterProxy); + m_tableView->setSortingEnabled(true); + + auto item_delegate = new PgLabItemDelegate(this); + m_tableView->setItemDelegate(item_delegate); + //auto icon_delegate = new IconColumnDelegate(this); + + connect(m_tableView->selectionModel(), &QItemSelectionModel::selectionChanged, + this, &TriggerPage::tableView_selectionChanged); +} + + +void TriggerPage::setCatalog(std::shared_ptr cat) +{ + m_catalog = cat; + m_definitionView->setCatalog(cat); + m_model->setCatalog(cat); +} + + +void TriggerPage::setFilter(const PgClass &cls) +{ + m_sortFilterProxy->setOidFilterTable(cls.oid, FirstHiddenValue); +} + + +void TriggerPage::tableView_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + auto&& indexes = m_tableView->selectionModel()->selectedIndexes(); + boost::container::flat_set rijen; + for (const auto &e : indexes) + rijen.insert(m_sortFilterProxy->mapToSource(e).row()); + + QString drops; + QString creates; + for (auto rij : rijen) { + auto&& t = m_model->trigger(rij); + drops += t.dropSql(*m_catalog) % "\n"; + creates += t.createSql(*m_catalog) % "\n"; + } + m_definitionView->setPlainText(drops % "\n" % creates); +} diff --git a/pglab/TriggerPage.h b/pglab/TriggerPage.h new file mode 100644 index 0000000..35cc4e3 --- /dev/null +++ b/pglab/TriggerPage.h @@ -0,0 +1,41 @@ +#ifndef TRIGGERPAGE_H +#define TRIGGERPAGE_H + +#include +#include +#include + +class QSplitter; +class QTableView; +class SqlCodePreview; +class PgDatabaseCatalog; +class PgClass; +class TriggerTableModel; +class CustomFilterSortModel; +class QItemSelection; + +class TriggerPage : public QSplitter +{ + Q_OBJECT +public: + explicit TriggerPage(QWidget *parent = nullptr); +// TriggerPage(QWidget *parent = nullptr); + + void setCatalog(std::shared_ptr cat); + void setFilter(const PgClass &cls); +signals: + +public slots: + +private: + QTableView *m_tableView = nullptr; + SqlCodePreview *m_definitionView = nullptr; + TriggerTableModel *m_model = nullptr; + CustomFilterSortModel *m_sortFilterProxy = nullptr; + std::shared_ptr m_catalog; + +private slots: + void tableView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); +}; + +#endif // TRIGGERPAGE_H diff --git a/pglab/TriggerTableModel.cpp b/pglab/TriggerTableModel.cpp new file mode 100644 index 0000000..8907487 --- /dev/null +++ b/pglab/TriggerTableModel.cpp @@ -0,0 +1,100 @@ +#include "TriggerTableModel.h" +#include "PgDatabaseCatalog.h" +#include "PgTriggerContainer.h" +#include "CustomDataRole.h" + +TriggerTableModel::TriggerTableModel(QObject *parent) + : QAbstractTableModel(parent) +{ +} + +QVariant TriggerTableModel::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 EventsCol: return tr("Events"); + case WhenCol: return tr("When"); + case DeferrableCol: return tr("Df"); + case InitiallyCol: return tr("ID"); + case ForCol: return tr("For"); + case CondCol: return tr("Cond."); + case ReferencingCol: return tr("Referencing"); + case ProcedureCol: return tr("Function"); + } + } + } + return QVariant(); +} + +void TriggerTableModel::setCatalog(std::shared_ptr cat) +{ + beginResetModel(); + + m_catalog = cat; + m_triggers = cat->triggers();//->getTriggersForRelation(table); + + endResetModel(); +} + +int TriggerTableModel::rowCount(const QModelIndex &) const +{ + return m_triggers ? static_cast(m_triggers->count()) : 0; +} + + +int TriggerTableModel::columnCount(const QModelIndex &) const +{ + return colCount; +} + + +QVariant TriggerTableModel::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 == FirstHiddenValue) { + auto&& t = m_triggers->getByIdx(index.row()); + return t.relid; // + } + return QVariant(); +} + + +PgTrigger TriggerTableModel::trigger(int row) const +{ + return m_triggers->getByIdx(row); +} + + +Oid TriggerTableModel::getType(int column) const +{ + switch (column) { + case DeferrableCol: + case InitiallyCol: + return Pgsql::bool_oid; + } + return Pgsql::varchar_oid; +} + + +QVariant TriggerTableModel::getData(const QModelIndex &index) const +{ + auto&& t = m_triggers->getByIdx(index.row()); + switch (index.column()) { + case NameCol: return t.name; + case EventsCol: return t.eventAbbr(); + case WhenCol: return t.typeFireWhen(); + case DeferrableCol: return t.deferrable; + case InitiallyCol: return t.initdeferred; + case ForCol: return t.forEach(); +// case CondCol: return tr("Cond."); +// case ReferencingCol: return tr("Referencing"); +// case ProcedureCol: return tr("Function"); + + } + return QVariant(); +} diff --git a/pglab/TriggerTableModel.h b/pglab/TriggerTableModel.h new file mode 100644 index 0000000..4fcc444 --- /dev/null +++ b/pglab/TriggerTableModel.h @@ -0,0 +1,75 @@ +#ifndef TRIGGERTABLEMODEL_H +#define TRIGGERTABLEMODEL_H + +#include +#include "PgClass.h" +#include "PgTrigger.h" +#include + +class PgDatabaseCatalog; +class PgTriggerContainer; + +/** + * @brief The TriggerTableModel class + * + * Hidden values: + * - FirstHiddenValue: oid of table the trigger belongs to + */ +class TriggerTableModel: public QAbstractTableModel { + Q_OBJECT +public: + //using QAbstractTableModel::QAbstractTableModel; + + enum e_Columns : int { + NameCol, // + EventsCol, // Insert, Update, Delete Truncate + WhenCol, // before after instead of + DeferrableCol, + InitiallyCol, + ForCol, + CondCol, + ReferencingCol, + ProcedureCol, + colCount + }; + + TriggerTableModel(QObject *parent = nullptr); + +// CREATE [ CONSTRAINT ] TRIGGER name { BEFORE | AFTER | INSTEAD OF } { event [ OR ... ] } +// ON table_name +// [ FROM referenced_table_name ] +// [ NOT DEFERRABLE | [ DEFERRABLE ] [ INITIALLY IMMEDIATE | INITIALLY DEFERRED ] ] +// [ REFERENCING { { OLD | NEW } TABLE [ AS ] transition_relation_name } [ ... ] ] +// [ FOR [ EACH ] { ROW | STATEMENT } ] +// [ WHEN ( condition ) ] +// EXECUTE PROCEDURE function_name ( arguments ) + +// where event can be one of: + +// INSERT +// UPDATE [ OF column_name [, ... ] ] +// DELETE +// TRUNCATE + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + void setCatalog(std::shared_ptr cat); + + + // Basic functionality: + int rowCount(const QModelIndex &parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + + virtual QVariant data(const QModelIndex &index, int role) const override; + + PgTrigger trigger(int row) const; + +private: + std::shared_ptr m_catalog; + std::shared_ptr m_triggers; + + Oid getType(int column) const; + QVariant getData(const QModelIndex &index) const; +}; + + +#endif // TRIGGERTABLEMODEL_H diff --git a/pglab/pglab.pro b/pglab/pglab.pro index be8e656..afb2036 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -78,7 +78,11 @@ SOURCES += main.cpp\ PlgPage.cpp \ PropertyProxyModel.cpp \ CodeGenerator.cpp \ - UserConfiguration.cpp + UserConfiguration.cpp \ + TriggerTableModel.cpp \ + TriggerPage.cpp \ + SqlCodePreview.cpp \ + CustomFilterSortModel.cpp HEADERS += \ QueryResultModel.h \ @@ -104,7 +108,6 @@ HEADERS += \ ConnectionList.h \ ProcessStdioWidget.h \ GlobalIoService.h \ - CodeBuilderConfiguration.h \ ResultTableModelUtil.h \ BaseTableModel.h \ QueryParamListController.h \ @@ -128,7 +131,11 @@ HEADERS += \ PropertyProxyModel.h \ CustomDataRole.h \ CodeGenerator.h \ - UserConfiguration.h + UserConfiguration.h \ + TriggerTableModel.h \ + TriggerPage.h \ + SqlCodePreview.h \ + CustomFilterSortModel.h FORMS += mainwindow.ui \ ConnectionManagerWindow.ui \ diff --git a/pglablib/PgDatabaseCatalog.cpp b/pglablib/PgDatabaseCatalog.cpp index 007fa2b..9e8a754 100644 --- a/pglablib/PgDatabaseCatalog.cpp +++ b/pglablib/PgDatabaseCatalog.cpp @@ -10,6 +10,7 @@ #include "PgIndexContainer.h" #include "PgNamespaceContainer.h" #include "PgTablespaceContainer.h" +#include "PgTriggerContainer.h" #include "PgTypeContainer.h" #include "Pgsql_Connection.h" #include "Pgsql_oids.h" @@ -134,7 +135,7 @@ void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn, std::function progress_callback) { loadInfo(conn); - const int count = 11; + const int count = 12; int n = 0; if (progress_callback && !progress_callback(++n, count)) return; @@ -163,10 +164,15 @@ void PgDatabaseCatalog::loadAll(Pgsql::Connection &conn, if (progress_callback && !progress_callback(++n, count)) return; load2(m_ams, conn); + if (progress_callback && !progress_callback(++n, count)) + return; + load2(m_triggers, conn); if (progress_callback && !progress_callback(++n, count)) return; load2(m_types, conn); progress_callback && progress_callback(++n, count); + + refreshed(this, All); } void PgDatabaseCatalog::loadInfo(Pgsql::Connection &conn) @@ -260,6 +266,11 @@ std::shared_ptr PgDatabaseCatalog::tablespaces() co return m_tablespaces; } +std::shared_ptr PgDatabaseCatalog::triggers() const +{ + return m_triggers; +} + std::shared_ptr PgDatabaseCatalog::types() const { return m_types; diff --git a/pglablib/PgDatabaseCatalog.h b/pglablib/PgDatabaseCatalog.h index ca1869c..963cd0b 100644 --- a/pglablib/PgDatabaseCatalog.h +++ b/pglablib/PgDatabaseCatalog.h @@ -2,8 +2,10 @@ #define PGSQLDATABASECATALOGUE_H #include +#include #include #include +#include #include #include @@ -22,9 +24,11 @@ class PgIndexContainer; class PgNamespaceContainer; class PgAmContainer; class PgTablespaceContainer; +class PgTriggerContainer; class PgTypeContainer; -class PgDatabaseCatalog: public std::enable_shared_from_this { +class PgDatabaseCatalog: public QObject, public std::enable_shared_from_this { + Q_OBJECT public: PgDatabaseCatalog(); PgDatabaseCatalog(const PgDatabaseCatalog&) = delete; @@ -49,8 +53,27 @@ public: std::shared_ptr ams() const; std::shared_ptr namespaces() const; std::shared_ptr tablespaces() const; + std::shared_ptr triggers() const; std::shared_ptr types() const; + enum RefreshFlag { + Attributes = 1, + AuthIds = (1 << 1), + Classes = (1 << 2), + Constraints = (1 << 3), + Databases = (1 << 4), + Indexes = (1 << 5), + Ams = (1 << 6), + Namespaces = (1 << 7), + Tablespaces = (1 << 8), + Triggers = (1 << 9), + Types = (1 << 10), + All = 0xffffffff + }; + using RefreshFlags = int; + +signals: + void refreshed(const PgDatabaseCatalog *catalog, RefreshFlags flags); private: QString m_serverVersionString; int m_serverVersion; @@ -65,6 +88,7 @@ private: std::shared_ptr m_ams; std::shared_ptr m_namespaces; std::shared_ptr m_tablespaces; + std::shared_ptr m_triggers; std::shared_ptr m_types; template diff --git a/pglablib/PgTrigger.cpp b/pglablib/PgTrigger.cpp index c2cab08..a7182b9 100644 --- a/pglablib/PgTrigger.cpp +++ b/pglablib/PgTrigger.cpp @@ -1,6 +1,126 @@ -#include "PgTrigger.h" +#include "PgTrigger.h" +#include "PgClassContainer.h" +#include "PgDatabaseCatalog.h" +#include "SqlFormattingUtils.h" +#include -PgTrigger::PgTrigger() + +QString PgTrigger::dropSql(const PgDatabaseCatalog &catalog) { - + if (m_dropSql.isEmpty()) { + auto&& fqtablename = genFQTableName(catalog, catalog.classes()->getByKey(relid)); + m_dropSql = "DROP TRIGGER " % quoteIdent(name) + % " ON " % fqtablename % ";"; + } + return m_dropSql; } + +QString PgTrigger::createSql(const PgDatabaseCatalog &catalog) +{ + if (m_createSql.isEmpty()) { + auto&& fqtablename = genFQTableName(catalog, catalog.classes()->getByKey(relid)); +// if (GetLanguage() == wxT("edbspl")) +// sql += wxT("CREATE OR REPLACE TRIGGER "); +// else if (GetConnection()->BackendMinimumVersion(8, 2) && GetIsConstraint()) + if (constraint != InvalidOid) + m_createSql += "CREATE CONSTRAINT TRIGGER "; + else + m_createSql += "CREATE TRIGGER "; + + m_createSql += quoteIdent(name) + "\n " + + typeFireWhen() + + " " + event(); + + m_createSql += "\n ON " + fqtablename; + if (deferrable) { + m_createSql += "\n DEFERRABLE INITIALLY "; + if (initdeferred) + m_createSql += "DEFERRED"; + else + m_createSql += "IMMEDIATE"; + } + m_createSql += "\n FOR EACH " + forEach(); + +// if (GetConnection()->BackendMinimumVersion(8, 5) +// && !GetWhen().IsEmpty()) +// sql += wxT("\n WHEN (") + GetWhen() + wxT(")"); + +// if (GetLanguage() == wxT("edbspl")) +// { +// sql += wxT("\n") + GetSource(); +// if (!sql.Trim().EndsWith(wxT(";"))) +// sql = sql.Trim() + wxT(";"); +// sql += wxT("\n"); +// } +// else +// { +// sql += wxT("\n EXECUTE PROCEDURE ") + triggerFunction->GetQuotedFullIdentifier() +// + wxT("(") + GetArguments() + wxT(")") +// + wxT(";\n"); +// } + +// if (!GetEnabled()) +// { +// sql += wxT("ALTER TABLE ") + GetQuotedFullTable() + wxT(" ") +// + wxT("DISABLE TRIGGER ") + GetQuotedIdentifier() + wxT(";\n"); +// } + +// if (!GetComment().IsEmpty()) +// sql += wxT("COMMENT ON TRIGGER ") + GetQuotedIdentifier() + wxT(" ON ") + GetQuotedFullTable() +// + wxT(" IS ") + qtDbString(GetComment()) + wxT(";\n"); + } + + return m_createSql; +} + + +QString PgTrigger::typeFireWhen() const +{ + QString when; + + if (type & TriggerTypeBefore) + when = "BEFORE"; + else if (type & TriggerTypeInstead) + when = "INSTEAD OF"; + else + when = "AFTER"; + return when; +} + + +QString PgTrigger::eventAbbr() const +{ + QString event; + if (type & TriggerTypeInsert) + event += "I"; + if (type & TriggerTypeUpdate) + event += "U"; + if (type & TriggerTypeDelete) + event += "D"; + if (type & TriggerTypeTruncate) + event += "T"; + return event; +} + +QString PgTrigger::event() const +{ + QString event; + if (type & TriggerTypeInsert) + event += "INSERT "; + if (type & TriggerTypeUpdate) + event += "UPDATE "; + if (type & TriggerTypeDelete) + event += "DELETE "; + if (type & TriggerTypeTruncate) + event += "TRUNCATE"; + return event.trimmed(); +} + +QString PgTrigger::forEach() const +{ + if (isRow()) + return "ROW"; + else + return "STATEMENT"; +} + diff --git a/pglablib/PgTrigger.h b/pglablib/PgTrigger.h index d3e2382..7592857 100644 --- a/pglablib/PgTrigger.h +++ b/pglablib/PgTrigger.h @@ -5,24 +5,57 @@ #include #include +class PgDatabaseCatalog; + class PgTrigger { public: - Oid tgrelid; - QString tgname; - Oid tgfoid; - int16_t tgtype; - char tgenabled; - bool tgisinternal; - Oid tgconstrrelid; - Oid tgconstrindid; - Oid tgconstraint; - bool tgdeferrable; - bool tginitdeferred; - int16_t tgnargs; - QString tgattr; - QString tgargs; - QString tgqual; + Oid oid = InvalidOid; + Oid relid; + QString name; + Oid foid; + int16_t type; + char enabled; + bool isinternal; + Oid constrrelid; + Oid constrindid; + Oid constraint; + bool deferrable; + bool initdeferred; + int16_t nargs; + QString attr; + QString args; + QString qual; + 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 PgTrigger &rhs) const { return oid < rhs.oid; } + + static constexpr int TriggerTypeRow = (1 << 0); + static constexpr int TriggerTypeBefore = (1 << 1); + static constexpr int TriggerTypeInsert = (1 << 2); + static constexpr int TriggerTypeDelete = (1 << 3); + static constexpr int TriggerTypeUpdate = (1 << 4); + static constexpr int TriggerTypeTruncate = (1 << 5); + static constexpr int TriggerTypeInstead = (1 << 6); + + QString dropSql(const PgDatabaseCatalog &catalog); + QString createSql(const PgDatabaseCatalog &catalog); + bool isRow() const { return type & TriggerTypeRow; } + bool isBefore() const { return type & TriggerTypeBefore; } + QString typeFireWhen() const; + QString eventAbbr() const; + QString event() const; + QString forEach() const; + + //wxString pgTrigger::GetForEach() const + //{ + // return (triggerType & TRIGGER_TYPE_ROW) ? wxT("ROW") : wxT("STATEMENT"); + //} + +private: + mutable QString m_dropSql; // cache + mutable QString m_createSql; // cache }; #endif // PGTRIGGER_H diff --git a/pglablib/PgTriggerContainer.cpp b/pglablib/PgTriggerContainer.cpp index 0134bef..79f9ca1 100644 --- a/pglablib/PgTriggerContainer.cpp +++ b/pglablib/PgTriggerContainer.cpp @@ -1,11 +1,12 @@ #include "PgTriggerContainer.h" #include "Pgsql_Connection.h" #include "Pgsql_Col.h" - +#include +#include std::string PgTriggerContainer::getLoadQuery() const { - return R"(SELECT * + return R"(SELECT oid, * FROM pg_trigger WHERE NOT tgisinternal)"; } @@ -14,8 +15,19 @@ PgTrigger PgTriggerContainer::loadElem(const Pgsql::Row &row) { Pgsql::Col col(row); PgTrigger v; - col >> v.tgrelid >> v.tgname >> v.tgfoid >> v.tgtype >> v.tgenabled >> v.tgisinternal >> v.tgconstrrelid - >> v.tgconstrindid >> v.tgconstraint >> v.tgdeferrable >> v.tginitdeferred >> v.tgnargs >> v.tgattr - >> v.tgargs >> v.tgqual; + col >> v.oid >> v.relid >> v.name >> v.foid >> v.type >> v.enabled >> v.isinternal >> v.constrrelid + >> v.constrindid >> v.constraint >> v.deferrable >> v.initdeferred >> v.nargs >> v.attr + >> v.args >> v.qual; return v; } + + +std::vector PgTriggerContainer::getTriggersForRelation(Oid cls) const +{ + std::vector result; + for (auto e : m_container) + if (e.relid == cls) + result.push_back(e); + + return result; +} diff --git a/pglablib/PgTriggerContainer.h b/pglablib/PgTriggerContainer.h index 78eb818..fe0e6e0 100644 --- a/pglablib/PgTriggerContainer.h +++ b/pglablib/PgTriggerContainer.h @@ -3,6 +3,7 @@ #include "PgContainer.h" #include "PgTrigger.h" +#include namespace Pgsql { @@ -16,6 +17,8 @@ public: using PgContainer::PgContainer; virtual std::string getLoadQuery() const override; + + std::vector getTriggersForRelation(Oid cls) const; protected: PgTrigger loadElem(const Pgsql::Row &row) override; private: diff --git a/pgsql/Pgsql_Value.cpp b/pgsql/Pgsql_Value.cpp index 4219887..b9ee9bf 100644 --- a/pgsql/Pgsql_Value.cpp +++ b/pgsql/Pgsql_Value.cpp @@ -57,7 +57,7 @@ Value::operator int16_t() const Value::operator int32_t() const { - const int len = std::strlen(m_val); + const size_t len = std::strlen(m_val); if (len > 0) { char *endptr = nullptr; long result = std::strtol(m_val, &endptr, 10); @@ -100,6 +100,11 @@ Value::operator double() const return std::stod(m_val); } +Value::operator char() const +{ + return m_val[0]; +} + bool Value::isString() const { diff --git a/pgsql/Pgsql_Value.h b/pgsql/Pgsql_Value.h index 31a7e61..21e6211 100644 --- a/pgsql/Pgsql_Value.h +++ b/pgsql/Pgsql_Value.h @@ -36,6 +36,7 @@ namespace Pgsql { operator bool() const; operator float() const; operator double() const; + operator char() const; bool isString() const;