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;