Improved error reporting

This commit is contained in:
eelke 2022-08-14 08:04:21 +02:00
parent 6cf7b52453
commit 457b09f15c
12 changed files with 110 additions and 94 deletions

View file

@ -254,13 +254,18 @@ void ConnectionConfigurationWidget::testConnection()
cc.setPassword(password); cc.setPassword(password);
auto qthis = QPointer(this); auto qthis = QPointer(this);
QFuture<void> result = QtConcurrent::run([cc] { QFuture<void> result = QtConcurrent::run(
return TestConnection(cc); [cc]
}).then(qApp, [qthis](TestConnectionResult result) { {
if (qthis) { return TestConnection(cc);
qthis.data()->handleTestResult(result);
} }
}); ).then(qApp,
[qthis](TestConnectionResult result)
{
if (qthis)
qthis.data()->handleTestResult(result);
}
);
} }
void ConnectionConfigurationWidget::handleTestResult(TestConnectionResult result) void ConnectionConfigurationWidget::handleTestResult(TestConnectionResult result)

View file

@ -118,6 +118,14 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config)
if (qthis) if (qthis)
qthis.data()->catalogLoaded(db); qthis.data()->catalogLoaded(db);
} }
).onFailed(qApp, [qthis](OpenDatabaseException &ex)
{
if (qthis) {
QMessageBox::critical(qthis.data(), "Error reading database",
ex.text());
qthis.data()->close();
}
}
); );
} }

View file

@ -9,7 +9,6 @@
#include "CustomDataRole.h" #include "CustomDataRole.h"
#include <QtConcurrent> #include <QtConcurrent>
#include <QFuture> #include <QFuture>
#include <QFutureWatcher>
#include <QMessageBox> #include <QMessageBox>
#include "Pgsql_oids.h" #include "Pgsql_oids.h"
#include "Pgsql_PgException.h" #include "Pgsql_PgException.h"
@ -247,9 +246,9 @@ void CrudModel::initRowMapping()
m_rowMapping.emplace_back(i); m_rowMapping.emplace_back(i);
} }
void CrudModel::connectionStateChanged(ASyncDBConnection::State state) void CrudModel::connectionStateChanged(ASyncDBConnection::StateData state)
{ {
switch (state) { switch (state.State) {
case ASyncDBConnection::State::NotConnected: case ASyncDBConnection::State::NotConnected:
break; break;
case ASyncDBConnection::State::Connecting: case ASyncDBConnection::State::Connecting:

View file

@ -259,7 +259,7 @@ private:
private slots: private slots:
void loadIntoModel(std::shared_ptr<Pgsql::Result> data); void loadIntoModel(std::shared_ptr<Pgsql::Result> data);
void connectionStateChanged(ASyncDBConnection::State state); void connectionStateChanged(ASyncDBConnection::StateData state);
}; };
#endif // CRUDMODEL_H #endif // CRUDMODEL_H

View file

@ -314,11 +314,12 @@ void QueryTool::queryTextChanged()
setQueryTextChanged(true); setQueryTextChanged(true);
} }
void QueryTool::connectionStateChanged(ASyncDBConnection::State state) void QueryTool::connectionStateChanged(ASyncDBConnection::StateData state)
{ {
QString iconname; QString iconname;
switch (state) { switch (state.State) {
case ASyncDBConnection::State::NotConnected: case ASyncDBConnection::State::NotConnected:
QMessageBox::warning(this, "pglab", tr("Warning connection and any of its session state has been lost"));
startConnect(); startConnect();
iconname = "red.png"; iconname = "red.png";
break; break;

View file

@ -113,7 +113,7 @@ private slots:
void query_ready(std::shared_ptr<Pgsql::Result>, qint64 elapsedms); void query_ready(std::shared_ptr<Pgsql::Result>, qint64 elapsedms);
void queryTextChanged(); void queryTextChanged();
void connectionStateChanged(ASyncDBConnection::State state); void connectionStateChanged(ASyncDBConnection::StateData state);
void receiveNotice(Pgsql::ErrorDetails notice); void receiveNotice(Pgsql::ErrorDetails notice);
void startConnect(); void startConnect();

View file

@ -79,10 +79,10 @@ private:
bool makeConnection(); bool makeConnection();
void communicate(); void communicate();
void doStateCallback(ASyncDBConnection::State state); void doStateCallback(ASyncDBConnection::StateData state);
/// Wait's for a command to come in and send's it to the server /// Wait's for a command to come in and send's it to the server
void waitForAndSendCommand(); void waitForAndSendCommand();
void doNewCommand(); void doNewCommand();
void waitForResult(); void waitForResult();
@ -106,33 +106,46 @@ void ASyncDBConnectionThread::run()
SCOPE_EXIT { SCOPE_EXIT {
m_state = ASyncDBConnection::State::NotConnected; m_state = ASyncDBConnection::State::NotConnected;
m_terminated = true; m_terminated = true;
m_connection.close();
// doStateCallback(ASyncDBConnection::State::NotConnected);
}; };
while (!terminateRequested) {
// make or recover connection try {
if (makeConnection()) { while (!terminateRequested) {
m_connection.setNoticeReceiver(
[this](const PGresult *result) { processNotice(result); }); // make or recover connection
m_canceller = m_connection.getCancel(); if (makeConnection()) {
m_connection.setNoticeReceiver(
[this](const PGresult *result) { processNotice(result); });
m_canceller = m_connection.getCancel();
// send commands and receive results // send commands and receive results
communicate(); communicate();
} }
else { else {
// It is not possible to determine the source of the problem. // It is not possible to determine the source of the problem.
// Accept for PQconnectionNeedsPassword // Accept for PQconnectionNeedsPassword
// Pass problem to main thread and stop this thread // Pass problem to main thread and stop this thread
// Main thread needs to know it has to restart connecting if it want's to. // 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 // TODO: add status functions to help main thread so it doesn't have to remember
// everything reported through callbacks. // everything reported through callbacks.
break; break;
} }
} }
// close connection doStateCallback({ ASyncDBConnection::State::NotConnected, QString("terminating") });
}
catch (const std::exception &ex)
{
doStateCallback({ ASyncDBConnection::State::NotConnected, QString::fromUtf8(ex.what()) });
}
catch (...)
{
doStateCallback({ ASyncDBConnection::State::NotConnected, QString::fromUtf8("Unknown error") });
}
} }
@ -146,19 +159,7 @@ bool ASyncDBConnectionThread::makeConnection()
using namespace std::literals::chrono_literals; using namespace std::literals::chrono_literals;
// start connecting // start connecting
// auto keywords = m_config.getKeywords();
// auto values = m_config.getValues();
QString conn_string = m_config.connectionString(); QString conn_string = m_config.connectionString();
#if false
try {
m_connection.connect(conn_string); //keywords, values, 0);
doStateCallback(ASyncDBConnection::State::Connected);
return true;
}
catch (Pgsql::PgConnectionError &) {
}
return false;
#else
while (!terminateRequested) { while (!terminateRequested) {
bool ok = m_connection.connectStart(conn_string); //keywords, values); bool ok = m_connection.connectStart(conn_string); //keywords, values);
@ -190,20 +191,21 @@ bool ASyncDBConnectionThread::makeConnection()
if (res == wait_result_socket_event) { if (res == wait_result_socket_event) {
auto poll_state = m_connection.connectPoll(); auto poll_state = m_connection.connectPoll();
if (poll_state == PGRES_POLLING_OK) { if (poll_state == PGRES_POLLING_OK) {
qDebug() << "ASyncDBConnection Connected";
// if connected return true // if connected return true
doStateCallback(ASyncDBConnection::State::Connected); doStateCallback({ ASyncDBConnection::State::Connected, "Success" });
return true; return true;
} }
else if (poll_state == PGRES_POLLING_FAILED) { else if (poll_state == PGRES_POLLING_FAILED) {
doStateCallback(ASyncDBConnection::State::NotConnected); doStateCallback({ ASyncDBConnection::State::NotConnected, "Failed to connect" });
return false; return false;
} }
else if (poll_state == PGRES_POLLING_READING) { else if (poll_state == PGRES_POLLING_READING) {
doStateCallback(ASyncDBConnection::State::Connecting); doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" });
fd = FD_READ; fd = FD_READ;
} }
else if (poll_state == PGRES_POLLING_WRITING) { else if (poll_state == PGRES_POLLING_WRITING) {
doStateCallback(ASyncDBConnection::State::Connecting); doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" });
fd = FD_WRITE; fd = FD_WRITE;
} }
} }
@ -216,7 +218,6 @@ bool ASyncDBConnectionThread::makeConnection()
} }
} }
return false; return false;
#endif
} }
void ASyncDBConnectionThread::communicate() void ASyncDBConnectionThread::communicate()
@ -252,13 +253,9 @@ void ASyncDBConnectionThread::stop()
m_stopEvent.set(); m_stopEvent.set();
} }
void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::State state) void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state)
{ {
m_state = state; m_state = state.State;
// std::lock_guard<std::mutex> lg(m_stateCallback.m_mutex);
// if (m_stateCallback.m_func) {
// m_stateCallback.m_func(state);
// }
Q_EMIT asyncConnObject->onStateChanged(state); Q_EMIT asyncConnObject->onStateChanged(state);
} }
@ -289,21 +286,13 @@ void ASyncDBConnectionThread::doNewCommand()
const Command &command = m_commandQueue.m_queue.front(); const Command &command = m_commandQueue.m_queue.front();
bool query_send = false; bool query_send = false;
if (command.params.empty()) if (command.params.empty())
query_send = m_connection.sendQuery(command.command.c_str()); m_connection.sendQuery(command.command.c_str());
else else
query_send = m_connection.sendQueryParams(command.command.c_str(), command.params); m_connection.sendQueryParams(command.command.c_str(), command.params);
if (query_send) { m_timer.start();
m_timer.start(); doStateCallback(ASyncDBConnection::State::QuerySend);
doStateCallback(ASyncDBConnection::State::QuerySend);
}
else {
std::string error = m_connection.getErrorMessage();
qDebug() << "Error sending query " << QString::fromStdString(error);
// todo: need to report the error
}
} }
} }
bool ASyncDBConnectionThread::consumeResultInput() bool ASyncDBConnectionThread::consumeResultInput()
@ -332,7 +321,7 @@ bool ASyncDBConnectionThread::consumeResultInput()
// error during consume // error during consume
auto error_msg = m_connection.getErrorMessage(); auto error_msg = m_connection.getErrorMessage();
qDebug() << "error while communicating with server " << QString::fromStdString(error_msg); qDebug() << "error while communicating with server " << QString::fromStdString(error_msg);
doStateCallback(ASyncDBConnection::State::NotConnected); doStateCallback({ASyncDBConnection::State::NotConnected, QString::fromStdString(error_msg)});
finished = true; finished = true;
stop(); stop();
} }

View file

@ -21,6 +21,7 @@ class ASyncDBConnectionThread;
class ASyncDBConnection: public QObject { class ASyncDBConnection: public QObject {
Q_OBJECT Q_OBJECT
public: public:
enum class State { enum class State {
NotConnected, NotConnected,
Connecting, Connecting,
@ -30,6 +31,17 @@ public:
Terminating ///< shutting down Terminating ///< shutting down
}; };
class StateData {
public:
State State;
QString Message;
StateData(enum State state, QString message = QString())
: State(state)
, Message(message)
{}
};
using on_result_callback = std::function<void(Expected<std::shared_ptr<Pgsql::Result>>, qint64)>; using on_result_callback = std::function<void(Expected<std::shared_ptr<Pgsql::Result>>, qint64)>;
explicit ASyncDBConnection(); explicit ASyncDBConnection();
@ -51,7 +63,7 @@ public:
bool cancel(); bool cancel();
Q_SIGNALS: Q_SIGNALS:
void onStateChanged(ASyncDBConnection::State state); void onStateChanged(ASyncDBConnection::StateData state);
void onNotice(Pgsql::ErrorDetails notice); void onNotice(Pgsql::ErrorDetails notice);
private: private:

View file

@ -115,19 +115,21 @@ Result Connection::queryParam(const QString &command, const Params &params)
} }
bool Connection::sendQuery(const char *query) void Connection::sendQuery(const char *query)
{ {
int res = PQsendQuery(conn, query); int res = PQsendQuery(conn, query);
return res == 1; if (res == 0)
throw PgConnectionError(PQerrorMessage(conn));
} }
bool Connection::sendQueryParams(const char * command, const Params &params) void Connection::sendQueryParams(const char * command, const Params &params)
{ {
int res = PQsendQueryParams(conn, command, params.size(), params.types(), int res = PQsendQueryParams(conn, command, params.size(), params.types(),
params.values(), params.lengths(), params.formats(), params.values(), params.lengths(), params.formats(),
0); // text format 0); // text format
return res == 1; if (res == 0)
throw PgConnectionError(PQerrorMessage(conn));
} }
std::shared_ptr<Result> Connection::getResult() std::shared_ptr<Result> Connection::getResult()

View file

@ -85,17 +85,17 @@ namespace Pgsql {
Result queryParam(const char * command, const Params &params); Result queryParam(const char * command, const Params &params);
Result queryParam(const QString &command, const Params &params); Result queryParam(const QString &command, const Params &params);
bool sendQuery(const char * query); void sendQuery(const char * query);
bool sendQuery(const std::string &command) void sendQuery(const std::string &command)
{ {
return sendQuery(command.c_str()); sendQuery(command.c_str());
} }
bool sendQuery(const QString &command) void sendQuery(const QString &command)
{ {
return sendQuery(command.toUtf8().data()); sendQuery(command.toUtf8().data());
} }
bool sendQueryParams(const char * command, const Params &params); void sendQueryParams(const char * command, const Params &params);
std::shared_ptr<Result> getResult(); std::shared_ptr<Result> getResult();
std::shared_ptr<Result> getResultNoThrow(); std::shared_ptr<Result> getResultNoThrow();

View file

@ -84,32 +84,32 @@ namespace Pgsql {
return m_conn->queryParam(command, params); return m_conn->queryParam(command, params);
} }
bool Transaction::sendQuery(const char * query) void Transaction::sendQuery(const char * query)
{ {
BOOST_ASSERT(m_conn != nullptr); BOOST_ASSERT(m_conn != nullptr);
BOOST_ASSERT(m_committed == false); BOOST_ASSERT(m_committed == false);
BOOST_ASSERT(m_rolledback == false); BOOST_ASSERT(m_rolledback == false);
return m_conn->sendQuery(query); m_conn->sendQuery(query);
} }
bool Transaction::sendQuery(const std::string &command) void Transaction::sendQuery(const std::string &command)
{ {
return sendQuery(command.c_str()); sendQuery(command.c_str());
} }
bool Transaction::sendQuery(const QString &command) void Transaction::sendQuery(const QString &command)
{ {
return sendQuery(command.toUtf8().data()); sendQuery(command.toUtf8().data());
} }
bool Transaction::sendQueryParams(const char * command, const Params &params) void Transaction::sendQueryParams(const char * command, const Params &params)
{ {
BOOST_ASSERT(m_conn != nullptr); BOOST_ASSERT(m_conn != nullptr);
BOOST_ASSERT(m_committed == false); BOOST_ASSERT(m_committed == false);
BOOST_ASSERT(m_rolledback == false); BOOST_ASSERT(m_rolledback == false);
return m_conn->sendQueryParams(command, params); m_conn->sendQueryParams(command, params);
} }
std::shared_ptr<Result> Transaction::getResult() std::shared_ptr<Result> Transaction::getResult()

View file

@ -25,10 +25,10 @@ namespace Pgsql {
Result query(const QString &command); Result query(const QString &command);
Result queryParam(const char * command, const Params &params); Result queryParam(const char * command, const Params &params);
Result queryParam(const QString &command, const Params &params); Result queryParam(const QString &command, const Params &params);
bool sendQuery(const char * query); void sendQuery(const char * query);
bool sendQuery(const std::string &command); void sendQuery(const std::string &command);
bool sendQuery(const QString &command); void sendQuery(const QString &command);
bool sendQueryParams(const char * command, const Params &params); void sendQueryParams(const char * command, const Params &params);
std::shared_ptr<Result> getResult(); std::shared_ptr<Result> getResult();
bool consumeInput(); bool consumeInput();
bool isBusy(); bool isBusy();