diff --git a/pglab/CrudModel.cpp b/pglab/CrudModel.cpp index 9942ad8..997ee1e 100644 --- a/pglab/CrudModel.cpp +++ b/pglab/CrudModel.cpp @@ -1,14 +1,25 @@ #include "CrudModel.h" +#include "ASyncWindow.h" #include "OpenDatabase.h" #include "PgDatabaseCatalog.h" #include "PgAttributeContainer.h" #include "PgConstraintContainer.h" +#include "GlobalIoService.h" +#include "SqlFormattingUtils.h" +#include "WorkManager.h" +#include +#include +#include +#include "Pgsql_oids.h" #include -CrudModel::CrudModel() - : m_dbConn(*getGlobalAsioIoService()) -{} +CrudModel::CrudModel(ASyncWindow *async_window) + : m_asyncWindow(async_window) + , m_dbConn(*getGlobalAsioIoService()) +{ + connect(&m_dbConn, &ASyncDBConnection::onStateChanged, this, &CrudModel::connectionStateChanged); +} CrudModel::~CrudModel() { @@ -21,11 +32,150 @@ CrudModel::~CrudModel() * when ordered by primary key, offset and limit work very quickly so we can get away with not loading * everything. */ -void CrudModel::setData(std::shared_ptr db, const PgClass &table) +void CrudModel::setConfig(std::shared_ptr db, const PgClass &table) { m_database = db; m_table = table; m_primaryKey = db->catalogue()->constraints()->getPrimaryForRelation(table.oid); //cat->attributes()->getColumnsForRelation() - m_dbConn.setupConnection(m_database.config()); + callLoadData = true; + m_dbConn.setupConnection(m_database->config()); +} + +QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + QVariant r; + if (role == Qt::DisplayRole) { + if (orientation == Qt::Horizontal) { + QString s(m_roData->getColName(section)); + s += "\n"; + s += getTypeDisplayString(*m_database->catalogue(), getType(section)); + r = s; + } + else { + r = QString::number(section + 1); + } + } + return r; +} + + +// Basic functionality: +int CrudModel::rowCount(const QModelIndex &parent) const +{ + + return m_roData ? m_roData->rows() : 0; +} + +int CrudModel::columnCount(const QModelIndex &parent) const +{ + return m_roData ? m_roData->cols() : 0; +} + +Oid CrudModel::getType(int column) const +{ + return m_roData ? m_roData->type(column) : InvalidOid; +} + +QVariant CrudModel::getData(const QModelIndex &index) const +{ + QVariant r; + if (m_roData) { + int rij = index.row(); + int col = index.column(); + if (m_roData->null(col, rij)) { + r = "null"; + } + else { + Oid o = m_roData->type(col); + auto value = m_roData->get(col, rij); + switch (o) { + case Pgsql::bool_oid: + r = bool(value); //s = (s == "t") ? "TRUE" : "FALSE"; + break; + default: { + QString s = value; + if (s.length() > 256) { + s.truncate(256); + } + r = s; + } + } + } + } + return r; +} + +QVariant CrudModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::ForegroundRole) { + int rij = index.row(); + int col = index.column(); + if (m_roData->null(col, rij)) { + return QBrush(Qt::gray); + } + } + else if (role == Qt::EditRole) { + return getData(index); + } + return BaseTableModel::data(index, role); +} + + +void CrudModel::loadData() +{ + QString table_name = genFQTableName(*m_database->catalogue(), m_table); + std::string q = "SELECT * FROM "; + q += table_name.toUtf8(); + m_dbConn.send(q, [this] (Expected> res, qint64) { + if (res.valid()) { + auto dbres = res.get(); + if (dbres && *dbres) { +// WorkManager::getWorkManager()->addWork( +// [dbres, this] () -> void { +// std::shared_ptr rl = resultToRowList(dbres); +// m_asyncWindow->QueueTask([this, rl]() { loadIntoModel(rl); }); +// }); + m_asyncWindow->QueueTask([this, dbres]() { loadIntoModel(dbres); }); + } + } + else { +// emit onQueryError(); + } + }); +} + +void CrudModel::loadIntoModel(std::shared_ptr data) +{ + beginResetModel(); + m_roData = data; + endResetModel(); +} + +void CrudModel::connectionStateChanged(ASyncDBConnection::State state) +{ + switch (state) { + case ASyncDBConnection::State::NotConnected: + break; + case ASyncDBConnection::State::Connecting: + break; + case ASyncDBConnection::State::Connected: + if (callLoadData) { + callLoadData = false; + loadData(); + } + break; + case ASyncDBConnection::State::QuerySend: + + break; + case ASyncDBConnection::State::CancelSend: + break; + case ASyncDBConnection::State::Terminating: + break; + } +} + +Qt::ItemFlags CrudModel::flags(const QModelIndex &) const +{ + return Qt::ItemIsSelectable + Qt::ItemIsEditable + Qt::ItemIsEnabled; } diff --git a/pglab/CrudModel.h b/pglab/CrudModel.h index df786f9..f04c705 100644 --- a/pglab/CrudModel.h +++ b/pglab/CrudModel.h @@ -4,13 +4,15 @@ #include "BaseTableModel.h" #include "ASyncDBConnection.h" #include "PgClass.h" +#include "PgConstraint.h" #include "Pgsql_Connection.h" #include #include #include class PgConstraint; -class OpenDatase; +class OpenDatabase; +class ASyncWindow; /** * @brief The CrudModel class @@ -33,35 +35,55 @@ class OpenDatase; class CrudModel: public BaseTableModel { Q_OBJECT public: - CrudModel(); + explicit CrudModel(ASyncWindow *async_win); ~CrudModel(); + void setConfig(std::shared_ptr db, const PgClass &table); + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - void setData(std::shared_ptr db, const PgClass &table); // 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; + virtual Qt::ItemFlags flags(const QModelIndex &) const override; protected: virtual Oid getType(int column) const override; virtual QVariant getData(const QModelIndex &index) const override; private: + ASyncWindow * m_asyncWindow; std::shared_ptr m_database; PgClass m_table; boost::optional m_primaryKey; ASyncDBConnection m_dbConn; + bool callLoadData = false; - using RowData = std::vector; - using RowDataPtr = std::unique_ptr; - /** Choosen for a vector of pointers while vector is relatively slow with insertion and removal in the middle - * I hope the small size of the elements will make this not much of an issue while the simple linear and sequential - * nature of a vector will keep memory allocation fairly efficient. - using RowList = std::vector; +// using RowData = std::vector; +// using RowDataPtr = std::unique_ptr; +// /** Choosen for a vector of pointers while vector is relatively slow with insertion and removal in the middle +// * I hope the small size of the elements will make this not much of an issue while the simple linear and sequential +// * nature of a vector will keep memory allocation fairly efficient. +// */ +// using RowList = std::vector; + std::shared_ptr m_roData; + void loadData(); + void loadIntoModel(std::shared_ptr data); + +// std::shared_ptr resultToRowList(std::shared_ptr result); + +private slots: + + void connectionStateChanged(ASyncDBConnection::State state); +// void queryResult(std::shared_ptr result); +// void queryError(); + +// void dataProcessingFutureFinished(); }; #endif // CRUDMODEL_H diff --git a/pglab/CrudTab.cpp b/pglab/CrudTab.cpp index abc61b2..21eb054 100644 --- a/pglab/CrudTab.cpp +++ b/pglab/CrudTab.cpp @@ -1,14 +1,38 @@ -#include "CrudTab.h" +#include "CrudTab.h" #include "ui_CrudTab.h" +#include "CrudModel.h" +#include "MainWindow.h" +#include "ResultTableModelUtil.h" -CrudTab::CrudTab(QWidget *parent) : - QWidget(parent), - ui(new Ui::CrudTab) + +CrudTab::CrudTab(MainWindow *parent) + : QWidget(parent) + , ui(new Ui::CrudTab) + , m_window(parent) { ui->setupUi(this); + + SetTableViewDefault(ui->tableView); + m_crudModel = new CrudModel(parent); + ui->tableView->setModel(m_crudModel); + } CrudTab::~CrudTab() { delete ui; } + +void CrudTab::setConfig(std::shared_ptr db, const PgClass &table) +{ + m_db = db; + m_table = table; +// m_catalog = cat; +// m_tablesModel->setCatalog(cat); +// ui->tableListTable->resizeColumnsToContents(); +// m_namespaceFilterWidget->init(cat->namespaces()); + +// auto highlighter = new SqlSyntaxHighlighter(ui->constraintSqlEdit->document()); +// highlighter->setTypes(*cat->types()); + m_crudModel->setConfig(db, table); +} diff --git a/pglab/CrudTab.h b/pglab/CrudTab.h index 95fd199..23cad8d 100644 --- a/pglab/CrudTab.h +++ b/pglab/CrudTab.h @@ -1,22 +1,37 @@ -#ifndef CRUDTAB_H +#ifndef CRUDTAB_H #define CRUDTAB_H +#include "PgClass.h" #include +#include namespace Ui { -class CrudTab; + class CrudTab; } +class OpenDatabase; +class CrudModel; +class MainWindow; + class CrudTab : public QWidget { Q_OBJECT public: - explicit CrudTab(QWidget *parent = 0); + explicit CrudTab(MainWindow *parent = 0); ~CrudTab(); + void setConfig(std::shared_ptr db, const PgClass &table); + private: Ui::CrudTab *ui; + + MainWindow *m_window; + + std::shared_ptr m_db; + PgClass m_table; + + CrudModel *m_crudModel = nullptr; }; #endif // CRUDTAB_H diff --git a/pglab/MainWindow.cpp b/pglab/MainWindow.cpp index 0d5cde6..c8e8c1b 100644 --- a/pglab/MainWindow.cpp +++ b/pglab/MainWindow.cpp @@ -14,6 +14,7 @@ #include "QueryTab.h" #include "util.h" #include "MasterController.h" +#include "CrudTab.h" #include "WorkManager.h" #include "ScopeGuard.h" @@ -45,7 +46,13 @@ QueryTab* MainWindow::newSqlPage() return qt; } - +void MainWindow::newCrudPage(const PgClass &table) +{ + CrudTab *ct = new CrudTab(this); + ct->setConfig(m_database, table); + ui->tabWidget->addTab(ct, "CRUD"); + ui->tabWidget->setCurrentWidget(ct); +} QueryTab *MainWindow::GetActiveQueryTab() { @@ -85,6 +92,7 @@ void MainWindow::catalogLoaded() ui->tabWidget->addTab(tt, "Tables"); ui->tabWidget->setCurrentWidget(tt); + newSqlPage(); } catch (std::runtime_error &ex) { QMessageBox::critical(this, "Error reading database", diff --git a/pglab/MainWindow.h b/pglab/MainWindow.h index 48123fc..6bd1876 100644 --- a/pglab/MainWindow.h +++ b/pglab/MainWindow.h @@ -32,6 +32,7 @@ class QueryTab; class MasterController; class QCloseEvent; class OpenDatabase; +class PgClass; class MainWindow : public ASyncWindow { Q_OBJECT @@ -42,6 +43,8 @@ public: void setConfig(const ConnectionConfig &config); std::shared_ptr getDatabase() { return m_database; } + + void newCrudPage(const PgClass &table); private: Ui::MainWindow *ui; diff --git a/pglab/TablesPage.cpp b/pglab/TablesPage.cpp index 02bf5fd..40c5913 100644 --- a/pglab/TablesPage.cpp +++ b/pglab/TablesPage.cpp @@ -14,10 +14,12 @@ #include "SqlSyntaxHighlighter.h" #include #include +#include "MainWindow.h" -TablesPage::TablesPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::TablesPage) +TablesPage::TablesPage(MainWindow *parent) + : QWidget(parent) + , ui(new Ui::TablesPage) + , m_window(parent) { ui->setupUi(this); @@ -121,3 +123,12 @@ void TablesPage::constraintsTable_selectionChanged(const QItemSelection &selecte } ui->constraintSqlEdit->setPlainText(drops % "\n" % creates); } + +void TablesPage::on_tableListTable_doubleClicked(const QModelIndex &index) +{ + PgClass table = m_tablesModel->getTable(index.row()); + if (table.oid != InvalidOid) { + m_window->newCrudPage(table); + } + +} diff --git a/pglab/TablesPage.h b/pglab/TablesPage.h index 3cfd30a..dd97b4c 100644 --- a/pglab/TablesPage.h +++ b/pglab/TablesPage.h @@ -15,18 +15,20 @@ class ConstraintModel; class PgDatabaseCatalog; class NamespaceFilterWidget; class IndexModel; +class MainWindow; class TablesPage : public QWidget { Q_OBJECT public: - explicit TablesPage(QWidget *parent = 0); + explicit TablesPage(MainWindow *parent = 0); ~TablesPage(); void setCatalog(std::shared_ptr cat); private: Ui::TablesPage *ui; + MainWindow *m_window; std::shared_ptr m_catalog; TablesTableModel* m_tablesModel = nullptr; ColumnTableModel* m_columnsModel = nullptr; @@ -39,6 +41,7 @@ private slots: void tableListTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void constraintsTable_currentRowChanged(const QModelIndex ¤t, const QModelIndex &previous); void constraintsTable_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void on_tableListTable_doubleClicked(const QModelIndex &index); }; #endif // TABLESPAGE_H diff --git a/pglab/pglab.pro b/pglab/pglab.pro index 511edfb..477ba26 100644 --- a/pglab/pglab.pro +++ b/pglab/pglab.pro @@ -5,7 +5,7 @@ #------------------------------------------------- CONFIG += c++17 -QT += core gui +QT += core gui concurrent greaterThan(QT_MAJOR_VERSION, 4): QT += widgets sql @@ -20,11 +20,11 @@ DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501 LIBS += -LC:\VSproj\boost32\lib -LC:/PROG/LIB -lws2_32 -llibpq #debug { -LIBS += c:/prog/lib/botand_imp.lib +LIBS += c:/prog/lib/botan_imp.lib #} #release { -#LIBS += c:\prog\lib\botan.lib +#LIBS += c:/prog/lib/botan_imp.lib #} win32:RC_ICONS += pglab.ico diff --git a/pglablib/ASyncDBConnection.cpp b/pglablib/ASyncDBConnection.cpp index 42b9b62..a8ae04e 100644 --- a/pglablib/ASyncDBConnection.cpp +++ b/pglablib/ASyncDBConnection.cpp @@ -12,7 +12,8 @@ namespace { { qRegisterMetaType(); qRegisterMetaType(); - } + qRegisterMetaType>(); + } } registerMetaTypes_instance; diff --git a/pglablib/ASyncDBConnection.h b/pglablib/ASyncDBConnection.h index 60543e9..a69e8ba 100644 --- a/pglablib/ASyncDBConnection.h +++ b/pglablib/ASyncDBConnection.h @@ -48,11 +48,28 @@ public: bool send(const std::string &command, on_result_callback on_result); bool send(const std::string &command, Pgsql::Params params, on_result_callback on_result); + /** This version of send uses the signal onQueryResult and onQueryError to report back + * the completion of the query. + */ + bool send(const std::string &command, Pgsql::Params params = Pgsql::Params()) + { + return send(command, params, [this] (Expected> res, qint64) { + if (res.valid()) { + emit onQueryResult(res.get()); + } + else { + emit onQueryError(); + } + }); + } + bool cancel(); signals: void onStateChanged(ASyncDBConnection::State state); void onNotice(Pgsql::ErrorDetails notice); + void onQueryResult(std::shared_ptr result); + void onQueryError(); private: Pgsql::Connection m_connection; @@ -71,6 +88,7 @@ private: Q_DECLARE_METATYPE(ASyncDBConnection::State); Q_DECLARE_METATYPE(Pgsql::ErrorDetails); +Q_DECLARE_METATYPE(std::shared_ptr); #endif // ASYNCDBCONNECTION_H diff --git a/pglablib/ParamListModel.cpp b/pglablib/ParamListModel.cpp index 931b15c..d8d4b81 100644 --- a/pglablib/ParamListModel.cpp +++ b/pglablib/ParamListModel.cpp @@ -7,7 +7,6 @@ ParamListModel::ParamListModel(QObject *parent) QVariant ParamListModel::headerData(int section, Qt::Orientation orientation, int role) const { - // FIXME: Implement me! QVariant result; if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { @@ -56,7 +55,7 @@ QVariant ParamListModel::data(const QModelIndex &index, int role) const if (index.isValid()) { int row = index.row(); int col = index.column(); - if (role == Qt::DisplayRole) { + if (role == Qt::DisplayRole || role == Qt::EditRole) { const auto& record = m_paramList[row]; switch (col) { case ColValue: // value column diff --git a/pglablib/SqlFormattingUtils.h b/pglablib/SqlFormattingUtils.h index a5d303e..903f314 100644 --- a/pglablib/SqlFormattingUtils.h +++ b/pglablib/SqlFormattingUtils.h @@ -3,12 +3,13 @@ #include - +class PgClass; class PgConstraint; class PgDatabaseCatalog; bool identNeedsQuotes(QString ident); +QString genFQTableName(const PgDatabaseCatalog &catalog, const PgClass &cls); QString getDropConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint); QString getConstraintDefinition(const PgDatabaseCatalog &catalog, const PgConstraint &constraint);