From a36bf5f7f4295ce989a998aa845af7a02b013919 Mon Sep 17 00:00:00 2001 From: Eelke Klein Date: Sun, 8 Jan 2017 09:58:34 +0100 Subject: [PATCH] Query, Explain and Cancel are going throught the asyncdbconnection now. Todo: Notice processing and error reporting. --- PgsqlConn.cpp | 12 +- PgsqlConn.h | 12 +- asyncdbconnection.cpp | 162 +++++++++++++++++++++--- asyncdbconnection.h | 42 +++++-- mainwindow.cpp | 283 ++++++++++++++++++------------------------ mainwindow.h | 18 ++- mainwindow.ui | 4 +- queryresultmodel.cpp | 10 +- queryresultmodel.h | 4 +- tsqueue.h | 1 + ui_mainwindow.h | 4 +- 11 files changed, 335 insertions(+), 217 deletions(-) diff --git a/PgsqlConn.cpp b/PgsqlConn.cpp index c745676..a0037cf 100644 --- a/PgsqlConn.cpp +++ b/PgsqlConn.cpp @@ -171,11 +171,15 @@ Canceller::~Canceller() } } -void Canceller::cancel() +bool Canceller::cancel(std::string *error) { const int errbuf_size = 256; char errbuf[errbuf_size]; - PQcancel(m_cancel, errbuf, errbuf_size); + bool res = PQcancel(m_cancel, errbuf, errbuf_size); + if (!res && error) { + *error = errbuf; + } + return res; } @@ -272,11 +276,11 @@ bool Connection::sendQuery(const char *query) return res == 1; } -std::unique_ptr Connection::getResult() +std::shared_ptr Connection::getResult() { PGresult *r = PQgetResult(conn); if (r) { - return std::make_unique(r); + return std::make_shared(r); } else { return nullptr; diff --git a/PgsqlConn.h b/PgsqlConn.h index 9941df5..1826f96 100644 --- a/PgsqlConn.h +++ b/PgsqlConn.h @@ -129,15 +129,17 @@ namespace Pgsql { class Canceller { public: + Canceller() = default; Canceller(PGcancel *c); Canceller(const Canceller&) = delete; Canceller& operator=(const Canceller&) = delete; Canceller(Canceller&& rhs); Canceller& operator=(Canceller&& rhs); ~Canceller(); - void cancel(); + + bool cancel(std::string *error); private: - PGcancel *m_cancel; + PGcancel *m_cancel = nullptr; }; @@ -187,12 +189,16 @@ namespace Pgsql { } bool sendQuery(const char * query); + bool sendQuery(const std::string &command) + { + return sendQuery(command.c_str()); + } bool sendQuery(const QString &command) { return sendQuery(command.toUtf8().data()); } - std::unique_ptr getResult(); + std::shared_ptr getResult(); bool consumeInput(); bool isBusy(); diff --git a/asyncdbconnection.cpp b/asyncdbconnection.cpp index 30ec76a..2c23490 100644 --- a/asyncdbconnection.cpp +++ b/asyncdbconnection.cpp @@ -19,10 +19,25 @@ void ASyncDBConnection::closeConnection() m_thread.join(); } +bool ASyncDBConnection::send(const std::string &command, on_result_callback on_result) +{ + { + std::lock_guard lg(m_threadData.m_commandQueue.m_mutex); + m_threadData.m_commandQueue.m_queue.emplace(command, on_result); + m_threadData.m_commandQueue.m_newEvent.set(); + } + return true; +} + +bool ASyncDBConnection::cancel() +{ + return m_threadData.cancel(); +} + void ASyncDBConnection::setStateCallback(on_state_callback state_callback) { - std::lock_guard lg(m_threadData.m_stateCallbackMutex); - m_threadData.m_stateCallback = state_callback; + std::lock_guard lg(m_threadData.m_stateCallback.m_mutex); + m_threadData.m_stateCallback.m_func = state_callback; } ASyncDBConnection::Thread::Thread() @@ -31,21 +46,39 @@ ASyncDBConnection::Thread::Thread() void ASyncDBConnection::Thread::run() { + m_terminated = false; while (!terminateRequested) { // make or recover connection if (makeConnection()) { + m_canceller = m_connection.getCancel(); // send commands and receive results communicate(); } + else { + // It is not possible to determine the source of the problem. + // Accept for PQconnectionNeedsPassword + // Pass problem to main thread and stop this thread + + // Main thread needs to know it has to restart connecting if it want's to. + // TODO: add status functions to help main thread so it doesn't have to remember + // everything reported through callbacks. + + break; + } } - + m_terminated = true; // close connection } +bool ASyncDBConnection::Thread::cancel() +{ + return m_canceller.cancel(nullptr); +} + bool ASyncDBConnection::Thread::makeConnection() { using namespace std::chrono_literals; @@ -122,18 +155,16 @@ void ASyncDBConnection::Thread::communicate() // - stop signal // - return -// WSAEventSelect(sock, socket_event.handle(), fd); - WaitHandleList whl; -// auto wait_result_socket_event = whl.add(socket_event); - auto wait_result_stop = whl.add(m_stopEvent); - DWORD res = MsgWaitForMultipleObjectsEx( - whl.count(), // _In_ DWORD nCount, - whl, // _In_ const HANDLE *pHandles, - INFINITE, // _In_ DWORD dwMilliseconds, - 0, // _In_ DWORD dwWakeMask, - 0 // _In_ DWORD dwFlags - ); + if (m_state == State::Connected) { + waitForAndSendCommand(); + } + else if (m_state == State::QuerySend || m_state == State::CancelSend) { + // Wait for result, even after a cancel we should wait, for all results + // New command's are not excepted when one has been send + waitForResult(); + } + } } @@ -145,8 +176,105 @@ void ASyncDBConnection::Thread::stop() void ASyncDBConnection::Thread::doStateCallback(State state) { - std::lock_guard lg(m_stateCallbackMutex); - if (m_stateCallback) { - m_stateCallback(state); + m_state = state; + std::lock_guard lg(m_stateCallback.m_mutex); + if (m_stateCallback.m_func) { + m_stateCallback.m_func(state); } } + +void ASyncDBConnection::Thread::waitForAndSendCommand() +{ + WaitHandleList whl; + auto wait_result_new_command = whl.add(m_commandQueue.m_newEvent); + auto wait_result_stop = whl.add(m_stopEvent); + + DWORD res = MsgWaitForMultipleObjectsEx( + whl.count(), // _In_ DWORD nCount, + whl, // _In_ const HANDLE *pHandles, + INFINITE, // _In_ DWORD dwMilliseconds, + 0, // _In_ DWORD dwWakeMask, + 0 // _In_ DWORD dwFlags + ); + if (res == wait_result_new_command) { + doNewCommand(); + } + // if (res == wait_result_stop) return; +} + +void ASyncDBConnection::Thread::doNewCommand() +{ + // todo: send command + // get command from top of queue (but leave it in the queue, we need the callback) + std::string command; + { + std::lock_guard lg(m_commandQueue.m_mutex); + if (! m_commandQueue.m_queue.empty()) { + command = m_commandQueue.m_queue.front().command; + } + } + if (!command.empty() && m_connection.sendQuery(command)) { + doStateCallback(State::QuerySend); + } + else { + std::string error = m_connection.getErrorMessage(); + // todo: need to report the error + } +} + +void ASyncDBConnection::Thread::waitForResult() +{ + + int sock = m_connection.socket(); + Win32Event socket_event(Win32Event::Reset::Auto, Win32Event::Initial::Clear); + + long fd = FD_WRITE; + + while (true) { + WSAEventSelect(sock, socket_event.handle(), fd); + + WaitHandleList whl; + auto wait_result_socket = whl.add(socket_event); + auto wait_result_stop = whl.add(m_stopEvent); + + DWORD res = MsgWaitForMultipleObjectsEx( + whl.count(), // _In_ DWORD nCount, + whl, // _In_ const HANDLE *pHandles, + INFINITE, // _In_ DWORD dwMilliseconds, + 0, // _In_ DWORD dwWakeMask, + 0 // _In_ DWORD dwFlags + ); + if (res == wait_result_socket) { + if (m_connection.consumeInput()) { + if ( ! m_connection.isBusy()) { + auto res(m_connection.getResult()); + { + std::lock_guard lg(m_commandQueue.m_mutex); + m_commandQueue.m_queue.front().on_result(res); + if (res == nullptr) { + m_commandQueue.m_queue.pop(); + doStateCallback(State::Connected); + break; // leave the while loop + + } + } + + } + // else is still waiting for more data + + } + else { + // error during consume + + } + } + if (res == wait_result_stop) { + // Send cancel, close connection and terminate thread + cancel(); + doStateCallback(State::Terminating); + break; + } + } // end while + // When last result received, remove command from queue +} + diff --git a/asyncdbconnection.h b/asyncdbconnection.h index 6c64826..888ad5f 100644 --- a/asyncdbconnection.h +++ b/asyncdbconnection.h @@ -5,6 +5,7 @@ #include "win32event.h" #include #include +#include #include #include @@ -15,10 +16,11 @@ public: Connecting, Connected, QuerySend, - CancelSend + CancelSend, + Terminating }; - using on_result_callback = std::function; + using on_result_callback = std::function)>; using on_state_callback = std::function; ASyncDBConnection(); @@ -30,26 +32,44 @@ public: /** Sends command to the server. - When the result is in and no result_task_queue was passed then on_result will be - called directly within the thread. + When the result is in on_result will be called directly within the thread. If the command gives multiple results on_result will be called for each result. */ bool send(const std::string &command, on_result_callback on_result); + bool cancel(); + private: class Command { public: std::string command; on_result_callback on_result; + + Command() = default; + Command(const std::string &cmd, on_result_callback cb) + : command(cmd), on_result(cb) + {} }; /// Contains all the members accessed by the thread class Thread { public: - on_state_callback m_stateCallback; - std::mutex m_stateCallbackMutex; + using t_CommandQueue = std::queue; + struct { + std::mutex m_mutex; + on_state_callback m_func; + } m_stateCallback; + + struct t_Command { + std::mutex m_mutex; + t_CommandQueue m_queue; + Win32Event m_newEvent; + t_Command() + : m_newEvent(Win32Event::Reset::Auto, Win32Event::Initial::Clear) + {} + } m_commandQueue; std::string m_initString; @@ -59,21 +79,29 @@ private: void run(); /// Sends a cancel request to the DB server - void cancel(); + bool cancel(); void stop(); private: + State m_state = State::NotConnected; + Win32Event m_stopEvent; Pgsql::Connection m_connection; bool terminateRequested = false; ///< is set when the thread should stop + bool m_terminated = true; + Pgsql::Canceller m_canceller; bool makeConnection(); void communicate(); void doStateCallback(State state); + /// Wait's for a command to come in and send's it to the server + void waitForAndSendCommand(); + void doNewCommand(); + void waitForResult(); }; Thread m_threadData; diff --git a/mainwindow.cpp b/mainwindow.cpp index 05e5506..c291e91 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -46,14 +46,14 @@ MainWindow::MainWindow(QWidget *parent) action = ui->mainToolBar->addAction("connect"); connect(action, &QAction::triggered, this, &MainWindow::startConnect); -// action = ui->mainToolBar->addAction("execute"); -// connect(action, &QAction::triggered, this, &MainWindow::performQuery); + action = ui->mainToolBar->addAction("execute"); + connect(action, &QAction::triggered, this, &MainWindow::performQuery); -// action = ui->mainToolBar->addAction("explain"); -// connect(action, &QAction::triggered, this, &MainWindow::performExplain); + action = ui->mainToolBar->addAction("explain"); + connect(action, &QAction::triggered, this, &MainWindow::performExplain); -// action = ui->mainToolBar->addAction("cancel"); -// connect(action, &QAction::triggered, this, &MainWindow::cancel_query); + action = ui->mainToolBar->addAction("cancel"); + connect(action, &QAction::triggered, this, &MainWindow::cancel_query); m_dbConnection.setStateCallback([this](ASyncDBConnection::State st) { @@ -112,174 +112,129 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state) void MainWindow::startConnect() { -// if (connection == nullptr) { -// connection = std::make_unique(); -// } -// QString connstr = ui->connectionStringEdit->text(); -// bool ok = connection->connectStart(connstr + " application_name=Ivory client_encoding=utf8"); -// if (ok && connection->status() != CONNECTION_BAD) { -// // Start polling -// int s = connection->socket(); - -// connectingState.notifier = std::make_unique(s, QSocketNotifier::Write); -// connect(connectingState.notifier.get(), &QSocketNotifier::activated, this, &MainWindow::socket_activate_connect); - -// connectingState.poll_state = PGRES_POLLING_WRITING; -// statusBar()->showMessage(tr("Connecting")); -// } -// else { -// statusBar()->showMessage(tr("Connecting fail")); -// } - std::string connstr = ui->connectionStringEdit->text().toUtf8().data(); m_dbConnection.setupConnection(connstr); } -//void MainWindow::socket_activate_connect(int ) -//{ -// connectingState.poll_state = connection->connectPoll(); +void MainWindow::performQuery() +{ + ui->ResultView->setModel(nullptr); + resultModel.reset(); + ui->messagesEdit->clear(); + QString command = ui->queryEdit->toPlainText(); + std::string cmd = command.toUtf8().data(); + m_dbConnection.send(cmd, + [this](std::shared_ptr res) + { + QueueTask([this, res]() { query_ready(res); }); + }); +} -// if (connectingState.poll_state == PGRES_POLLING_OK) { -// connection->setNoticeReceiver( -// std::bind(&MainWindow::processNotice, this, std::placeholders::_1)); -// statusBar()->showMessage(tr("Connected")); -// connectingState.notifier.reset(); -// } -// else if (connectingState.poll_state == PGRES_POLLING_FAILED) { -// statusBar()->showMessage(tr("Connection failed")); -// connectingState.notifier.reset(); -// } -// else if (connectingState.poll_state == PGRES_POLLING_READING) { -// statusBar()->showMessage(tr("Connecting..")); -// connectingState.notifier = std::make_unique(connection->socket(), QSocketNotifier::Read); -// connect(connectingState.notifier.get(), &QSocketNotifier::activated, this, &MainWindow::socket_activate_connect); -// } -// else if (connectingState.poll_state == PGRES_POLLING_WRITING) { -// statusBar()->showMessage(tr("Connecting...")); -// connectingState.notifier = std::make_unique(connection->socket(), QSocketNotifier::Write); -// connect(connectingState.notifier.get(), &QSocketNotifier::activated, this, &MainWindow::socket_activate_connect); -// } -//} +void MainWindow::query_ready(std::shared_ptr dbres) +{ + if (dbres) { + auto st = dbres->resultStatus(); + if (st == PGRES_TUPLES_OK) { + resultModel.reset(new QueryResultModel(nullptr , dbres)); + ui->ResultView->setModel(resultModel.get()); + statusBar()->showMessage(tr("Query ready.")); + } + else { + if (st == PGRES_EMPTY_QUERY) { + statusBar()->showMessage(tr("Empty query.")); + } + else if (st == PGRES_COMMAND_OK) { + statusBar()->showMessage(tr("Command OK.")); + } + else if (st == PGRES_COPY_OUT) { + statusBar()->showMessage(tr("COPY OUT.")); + } + else if (st == PGRES_COPY_IN) { + statusBar()->showMessage(tr("COPY IN.")); + } + else if (st == PGRES_BAD_RESPONSE) { + statusBar()->showMessage(tr("BAD RESPONSE.")); + } + else if (st == PGRES_NONFATAL_ERROR) { + statusBar()->showMessage(tr("NON FATAL ERROR.")); + } + else if (st == PGRES_FATAL_ERROR) { + statusBar()->showMessage(tr("FATAL ERROR.")); + } + else if (st == PGRES_COPY_BOTH) { + statusBar()->showMessage(tr("COPY BOTH shouldn't happen is for replication.")); + } + else if (st == PGRES_SINGLE_TUPLE) { + statusBar()->showMessage(tr("SINGLE TUPLE result.")); + } + else { + statusBar()->showMessage(tr("No tuples returned, possibly an error...")); + } + //receiveNotice(dbres->diagDetails()); + } + } + else { + statusBar()->showMessage(tr("Query cancelled.")); + } +} +void MainWindow::performExplain() +{ + ui->ResultView->setModel(nullptr); + resultModel.reset(); + ui->messagesEdit->clear(); -//void MainWindow::performQuery() -//{ -// ui->ResultView->setModel(nullptr); -// resultModel.reset(); -// ui->messagesEdit->clear(); + QString command = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) "; + command += ui->queryEdit->toPlainText(); + std::string cmd = command.toUtf8().data(); + m_dbConnection.send(cmd, + [this](std::shared_ptr res) + { + if (res) { + // Process explain data seperately + std::thread([this,res]() + { + std::shared_ptr explain; + if (res->getCols() == 1 && res->getRows() == 1) { + std::string s = res->getVal(0, 0); + Json::Value root; // will contains the root value after parsing. + Json::Reader reader; + bool parsingSuccessful = reader.parse(s, root); + if (parsingSuccessful) { + explain = ExplainRoot::createFromJson(root); + } + } + QueueTask([this, explain]() { explain_ready(explain); }); + }).detach(); + } + }); +} -// queryCancel = std::move(connection->getCancel()); +void MainWindow::explain_ready(ExplainRoot::SPtr explain) +{ + if (explain) { + explainModel.reset(new QueryExplainModel(nullptr, explain)); + ui->explainTreeView->setModel(explainModel.get()); + ui->explainTreeView->expandAll(); + ui->explainTreeView->setColumnWidth(0, 200); + ui->explainTreeView->setColumnWidth(1, 80); + ui->explainTreeView->setColumnWidth(2, 80); + ui->explainTreeView->setColumnWidth(3, 80); + ui->explainTreeView->setColumnWidth(4, 80); + ui->explainTreeView->setColumnWidth(5, 80); + ui->explainTreeView->setColumnWidth(6, 600); + statusBar()->showMessage(tr("Explain ready.")); + } + else { + statusBar()->showMessage(tr("Explain failed.")); + } +} -// QString command = ui->queryEdit->toPlainText(); - -// std::thread([this,command]() -// { -// auto res = std::make_shared(connection->query(command)); -// QueueTask([this, res]() { query_ready(std::move(*res)); }); -// }).detach(); -//} - -//void MainWindow::query_ready(Pgsql::Result dbres) -//{ -// if (dbres) { -// auto st = dbres.resultStatus(); -// if (st == PGRES_TUPLES_OK) { -// resultModel.reset(new QueryResultModel(nullptr , std::move(dbres))); -// ui->ResultView->setModel(resultModel.get()); -// statusBar()->showMessage(tr("Query ready.")); -// } -// else { -// if (st == PGRES_EMPTY_QUERY) { -// statusBar()->showMessage(tr("Empty query.")); -// } -// else if (st == PGRES_COMMAND_OK) { -// statusBar()->showMessage(tr("Command OK.")); -// } -// else if (st == PGRES_COPY_OUT) { -// statusBar()->showMessage(tr("COPY OUT.")); -// } -// else if (st == PGRES_COPY_IN) { -// statusBar()->showMessage(tr("COPY IN.")); -// } -// else if (st == PGRES_BAD_RESPONSE) { -// statusBar()->showMessage(tr("BEAD RESPONSE.")); -// } -// else if (st == PGRES_NONFATAL_ERROR) { -// statusBar()->showMessage(tr("NON FATAL ERROR.")); -// } -// else if (st == PGRES_FATAL_ERROR) { -// statusBar()->showMessage(tr("FATAL ERROR.")); -// } -// else if (st == PGRES_COPY_BOTH) { -// statusBar()->showMessage(tr("COPY BOTH shouldn't happen is for replication.")); -// } -// else if (st == PGRES_SINGLE_TUPLE) { -// statusBar()->showMessage(tr("SINGLE TUPLE result.")); -// } -// else { -// statusBar()->showMessage(tr("No tuples returned, possibly an error...")); -// } -// receiveNotice(dbres.diagDetails()); -// } -// } -// else { -// statusBar()->showMessage(tr("Query cancelled.")); -// } -//} - -//void MainWindow::performExplain() -//{ -// ui->ResultView->setModel(nullptr); -// resultModel.reset(); -// ui->messagesEdit->clear(); - -// queryCancel = std::move(connection->getCancel()); - -// QString command = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) "; -// command += ui->queryEdit->toPlainText(); -//// explainFuture = std::async(std::launch::async, [this,command]()-> std::unique_ptr -// std::thread([this,command]() -// { -// std::shared_ptr explain; -// auto res = connection->query(command); -// if (res.getCols() == 1 && res.getRows() == 1) { -// std::string s = res.getVal(0, 0); -// Json::Value root; // will contains the root value after parsing. -// Json::Reader reader; -// bool parsingSuccessful = reader.parse(s, root); -// if (parsingSuccessful) { -// explain = ExplainRoot::createFromJson(root); -// } -// } -// QueueTask([this, explain]() { explain_ready(explain); }); -// }).detach(); -//} - -//void MainWindow::explain_ready(ExplainRoot::SPtr explain) -//{ -// if (explain) { -// explainModel.reset(new QueryExplainModel(nullptr, explain)); -// ui->explainTreeView->setModel(explainModel.get()); -// ui->explainTreeView->expandAll(); -// ui->explainTreeView->setColumnWidth(0, 200); -// ui->explainTreeView->setColumnWidth(1, 80); -// ui->explainTreeView->setColumnWidth(2, 80); -// ui->explainTreeView->setColumnWidth(3, 80); -// ui->explainTreeView->setColumnWidth(4, 80); -// ui->explainTreeView->setColumnWidth(5, 80); -// ui->explainTreeView->setColumnWidth(6, 600); -// statusBar()->showMessage(tr("Explain ready.")); -// } -// else { -// statusBar()->showMessage(tr("Explain failed.")); -// } -//} - -//void MainWindow::cancel_query() -//{ -// queryCancel.cancel(); -//} +void MainWindow::cancel_query() +{ + m_dbConnection.cancel(); +} //void MainWindow::processNotice(const PGresult *result) //{ diff --git a/mainwindow.h b/mainwindow.h index efdd7b8..9a724f2 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -57,12 +57,8 @@ private: void connectionStateChanged(ASyncDBConnection::State state); -// std::unique_ptr connection; -// std::unique_ptr resultModel; -// std::unique_ptr explainModel; - -// Pgsql::Canceller queryCancel; - + std::unique_ptr resultModel; + std::unique_ptr explainModel; // struct { // std::unique_ptr notifier; @@ -70,17 +66,17 @@ private: // } connectingState; // void processNotice(const PGresult *result); -// void query_ready(Pgsql::Result res); -// void explain_ready(std::shared_ptr explain); + void query_ready(std::shared_ptr res); + void explain_ready(std::shared_ptr explain); private slots: void startConnect(); -// void performQuery(); -// void performExplain(); + void performQuery(); + void performExplain(); // void socket_activate_connect(int socket); -// void cancel_query(); + void cancel_query(); // void receiveNotice(Pgsql::ErrorDetails notice); void processCallableQueue(); diff --git a/mainwindow.ui b/mainwindow.ui index e024d51..f2c9d9a 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -38,7 +38,7 @@ - 2 + 1 @@ -147,7 +147,7 @@ 0 0 726 - 26 + 25 diff --git a/queryresultmodel.cpp b/queryresultmodel.cpp index b154b3f..45545e4 100644 --- a/queryresultmodel.cpp +++ b/queryresultmodel.cpp @@ -1,6 +1,6 @@ #include "queryresultmodel.h" -QueryResultModel::QueryResultModel(QObject *parent, Pgsql::Result &&r) +QueryResultModel::QueryResultModel(QObject *parent, std::shared_ptr r) : QAbstractTableModel(parent) , result(std::move(r)) {} @@ -8,13 +8,13 @@ QueryResultModel::QueryResultModel(QObject *parent, Pgsql::Result &&r) int QueryResultModel::rowCount(const QModelIndex &) const { - int r = result.getRows(); + int r = result->getRows(); return r; } int QueryResultModel::columnCount(const QModelIndex &) const { - int r = result.getCols(); + int r = result->getCols(); return r; } @@ -25,7 +25,7 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const int col = index.column(); if (role == Qt::DisplayRole) { - r = QString(result.getVal(col, rij)); + r = QString(result->getVal(col, rij)); } else if (role == Qt::TextAlignmentRole) { if (col == 0) { @@ -43,7 +43,7 @@ QVariant QueryResultModel::headerData(int section, Qt::Orientation orientation, QVariant r; if (role == Qt::DisplayRole) { if (orientation == Qt::Horizontal) { - r = QString(result.getColName(section)); + r = QString(result->getColName(section)); } else { r = QString::number(section + 1); diff --git a/queryresultmodel.h b/queryresultmodel.h index 1d52f85..f137293 100644 --- a/queryresultmodel.h +++ b/queryresultmodel.h @@ -8,7 +8,7 @@ class QueryResultModel : public QAbstractTableModel { Q_OBJECT public: - QueryResultModel(QObject *parent, Pgsql::Result &&r); + QueryResultModel(QObject *parent, std::shared_ptr r); int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; @@ -17,7 +17,7 @@ public: // virtual Qt::ItemFlags flags(const QModelIndex &index) const override; private: - Pgsql::Result result; + std::shared_ptr result; }; #endif // QUERYRESULTMODEL_H diff --git a/tsqueue.h b/tsqueue.h index 70d7ca0..acb7934 100644 --- a/tsqueue.h +++ b/tsqueue.h @@ -24,4 +24,5 @@ private: t_CallableQueue futureQueue; }; + #endif // TSQUEUE_H diff --git a/ui_mainwindow.h b/ui_mainwindow.h index 795b043..e5ecbc2 100644 --- a/ui_mainwindow.h +++ b/ui_mainwindow.h @@ -136,7 +136,7 @@ public: MainWindow->setCentralWidget(centralWidget); menuBar = new QMenuBar(MainWindow); menuBar->setObjectName(QStringLiteral("menuBar")); - menuBar->setGeometry(QRect(0, 0, 726, 26)); + menuBar->setGeometry(QRect(0, 0, 726, 25)); menuTest = new QMenu(menuBar); menuTest->setObjectName(QStringLiteral("menuTest")); MainWindow->setMenuBar(menuBar); @@ -151,7 +151,7 @@ public: retranslateUi(MainWindow); - tabWidget->setCurrentIndex(2); + tabWidget->setCurrentIndex(1); QMetaObject::connectSlotsByName(MainWindow);