Connection manager can now open a query window for selected connection.

Query window has now buttons with icons made in the designer for better looks.
Depending on received responses from the database the tabcontrol with the message, data and explain tab
now switches to the appropriate tab.
This commit is contained in:
Eelke Klein 2017-01-15 21:01:40 +01:00
parent 88fcc0338d
commit d19741f111
26 changed files with 408 additions and 116 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ DIST/
Makefile Makefile
Makefile.Debug Makefile.Debug
Makefile.Release Makefile.Release
desktop.ini

View file

@ -236,6 +236,13 @@ bool Connection::connectStart(const char* params)
return conn != nullptr; return conn != nullptr;
} }
bool Connection::connectStart(const char * const *keywords,
const char * const *values)
{
conn = PQconnectStartParams(keywords, values, 0);
return conn != nullptr;
}
PostgresPollingStatusType Connection::connectPoll() PostgresPollingStatusType Connection::connectPoll()
{ {
return PQconnectPoll(conn); return PQconnectPoll(conn);

View file

@ -19,18 +19,18 @@ namespace Pgsql {
*/ */
class ConnectionParams { // class ConnectionParams {
public: // public:
std::string host; // std::string host;
std::string hostaddr; // std::string hostaddr;
unsigned short port = 5432; // unsigned short port = 5432;
std::string dbname; // std::string dbname;
std::string user; // std::string user;
std::string password; // std::string password;
int connect_timeout = -1; ///< -1 omit (ie uses default) // int connect_timeout = -1; ///< -1 omit (ie uses default)
std::string application_name; // std::string application_name;
}; // };
class ErrorDetails { class ErrorDetails {
public: public:
@ -172,6 +172,8 @@ namespace Pgsql {
{ {
return connectStart(params.toUtf8().data()); return connectStart(params.toUtf8().data());
} }
bool connectStart(const char * const *keywords,
const char * const *values);
PostgresPollingStatusType connectPoll(); PostgresPollingStatusType connectPoll();
ConnStatusType status(); ConnStatusType status();

View file

@ -7,13 +7,23 @@ ASyncDBConnection::ASyncDBConnection()
} }
void ASyncDBConnection::setupConnection(const std::string &connstring) //void ASyncDBConnection::setupConnection(const std::string &connstring)
//{
// if (m_thread.joinable()) {
// m_threadData.stop();
// m_thread.join();
// }
// m_threadData.m_initString = connstring;
// m_thread = std::thread([this] () { m_threadData.run(); });
//}
void ASyncDBConnection::setupConnection(const ConnectionConfig &config)
{ {
if (m_thread.joinable()) { if (m_thread.joinable()) {
m_threadData.stop(); m_threadData.stop();
m_thread.join(); m_thread.join();
} }
m_threadData.m_initString = connstring; m_threadData.m_config = config;
m_thread = std::thread([this] () { m_threadData.run(); }); m_thread = std::thread([this] () { m_threadData.run(); });
} }
@ -100,7 +110,10 @@ bool ASyncDBConnection::Thread::makeConnection()
while (!terminateRequested) { while (!terminateRequested) {
// start connecting // start connecting
bool ok = m_connection.connectStart(m_initString + " client_encoding=utf8"); //bool ok = m_connection.connectStart(m_initString + " client_encoding=utf8");
auto keywords = m_config.getKeywords();
auto values = m_config.getValues();
bool ok = m_connection.connectStart(keywords, values);
auto start = std::chrono::steady_clock::now(); auto start = std::chrono::steady_clock::now();
if (ok && m_connection.status() != CONNECTION_BAD) { if (ok && m_connection.status() != CONNECTION_BAD) {
int sock = m_connection.socket(); int sock = m_connection.socket();

View file

@ -3,6 +3,7 @@
#include "PgsqlConn.h" #include "PgsqlConn.h"
#include "win32event.h" #include "win32event.h"
#include "connectionconfig.h"
#include <functional> #include <functional>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
@ -26,7 +27,8 @@ public:
ASyncDBConnection(); ASyncDBConnection();
void setupConnection(const std::string &connstring); // void setupConnection(const std::string &connstring);
void setupConnection(const ConnectionConfig &config);
void closeConnection(); void closeConnection();
void setStateCallback(on_state_callback state_callback); void setStateCallback(on_state_callback state_callback);
@ -77,7 +79,8 @@ private:
{} {}
} m_commandQueue; } m_commandQueue;
std::string m_initString; // std::string m_initString;
ConnectionConfig m_config;
Thread(); Thread();

View file

@ -53,7 +53,6 @@ std::vector<const char*> ConnectionConfig::s_keywords = {
ConnectionConfig::ConnectionConfig() ConnectionConfig::ConnectionConfig()
: m_applicationName(QCoreApplication::applicationName().toUtf8().data()) : m_applicationName(QCoreApplication::applicationName().toUtf8().data())
, m_values(s_keywords.size(), nullptr)
{} {}
void ConnectionConfig::setName(std::string desc) void ConnectionConfig::setName(std::string desc)
@ -69,7 +68,6 @@ const std::string& ConnectionConfig::name() const
void ConnectionConfig::setHost(std::string host) void ConnectionConfig::setHost(std::string host)
{ {
m_host = std::move(host); m_host = std::move(host);
m_values[0] = valuePtr(m_host);
} }
const std::string& ConnectionConfig::host() const const std::string& ConnectionConfig::host() const
@ -80,7 +78,6 @@ const std::string& ConnectionConfig::host() const
void ConnectionConfig::setHostAddr(std::string v) void ConnectionConfig::setHostAddr(std::string v)
{ {
m_hostaddr = std::move(v); m_hostaddr = std::move(v);
m_values[1] = valuePtr(m_hostaddr);
} }
const std::string& ConnectionConfig::hostAddr() const const std::string& ConnectionConfig::hostAddr() const
@ -91,7 +88,6 @@ const std::string& ConnectionConfig::hostAddr() const
void ConnectionConfig::setPort(unsigned short port) void ConnectionConfig::setPort(unsigned short port)
{ {
m_port = std::to_string(port); m_port = std::to_string(port);
m_values[2] = valuePtr(m_port);
} }
unsigned short ConnectionConfig::port() const unsigned short ConnectionConfig::port() const
@ -102,7 +98,6 @@ unsigned short ConnectionConfig::port() const
void ConnectionConfig::setUser(std::string v) void ConnectionConfig::setUser(std::string v)
{ {
m_user = std::move(v); m_user = std::move(v);
m_values[3] = valuePtr(m_user);
} }
const std::string& ConnectionConfig::user() const const std::string& ConnectionConfig::user() const
@ -113,7 +108,6 @@ const std::string& ConnectionConfig::user() const
void ConnectionConfig::setPassword(std::string v) void ConnectionConfig::setPassword(std::string v)
{ {
m_password = std::move(v); m_password = std::move(v);
m_values[4] = valuePtr(m_password);
} }
const std::string& ConnectionConfig::password() const const std::string& ConnectionConfig::password() const
@ -124,7 +118,6 @@ const std::string& ConnectionConfig::password() const
void ConnectionConfig::setDbname(std::string v) void ConnectionConfig::setDbname(std::string v)
{ {
m_dbname = std::move(v); m_dbname = std::move(v);
m_values[5] = valuePtr(m_dbname);
} }
const std::string& ConnectionConfig::dbname() const const std::string& ConnectionConfig::dbname() const
@ -135,7 +128,6 @@ const std::string& ConnectionConfig::dbname() const
void ConnectionConfig::setSslMode(SslMode m) void ConnectionConfig::setSslMode(SslMode m)
{ {
m_sslMode = SslModeToString(m); m_sslMode = SslModeToString(m);
m_values[6] = valuePtr(m_sslMode);
} }
SslMode ConnectionConfig::sslMode() const SslMode ConnectionConfig::sslMode() const
@ -146,7 +138,6 @@ SslMode ConnectionConfig::sslMode() const
void ConnectionConfig::setSslCert(std::string v) void ConnectionConfig::setSslCert(std::string v)
{ {
m_sslCert = std::move(v); m_sslCert = std::move(v);
m_values[7] = valuePtr(m_sslCert);
} }
const std::string& ConnectionConfig::sslCert() const const std::string& ConnectionConfig::sslCert() const
@ -157,7 +148,6 @@ const std::string& ConnectionConfig::sslCert() const
void ConnectionConfig::setSslKey(std::string v) void ConnectionConfig::setSslKey(std::string v)
{ {
m_sslKey = std::move(v); m_sslKey = std::move(v);
m_values[8] = valuePtr(m_sslKey);
} }
const std::string& ConnectionConfig::sslKey() const const std::string& ConnectionConfig::sslKey() const
@ -168,7 +158,6 @@ const std::string& ConnectionConfig::sslKey() const
void ConnectionConfig::setSslRootCert(std::string v) void ConnectionConfig::setSslRootCert(std::string v)
{ {
m_sslRootCert = std::move(v); m_sslRootCert = std::move(v);
m_values[9] = valuePtr(m_sslRootCert);
} }
const std::string& ConnectionConfig::sslRootCert() const const std::string& ConnectionConfig::sslRootCert() const
@ -179,7 +168,6 @@ const std::string& ConnectionConfig::sslRootCert() const
void ConnectionConfig::setSslCrl(std::string v) void ConnectionConfig::setSslCrl(std::string v)
{ {
m_sslCrl = std::move(v); m_sslCrl = std::move(v);
m_values[10] = valuePtr(m_sslCrl);
} }
const std::string& ConnectionConfig::sslCrl() const const std::string& ConnectionConfig::sslCrl() const
@ -195,6 +183,18 @@ const char * const * ConnectionConfig::getKeywords() const
const char * const * ConnectionConfig::getValues() const const char * const * ConnectionConfig::getValues() const
{ {
m_values.resize(s_keywords.size(), nullptr);
m_values[0] = valuePtr(m_host);
m_values[1] = valuePtr(m_hostaddr);
m_values[2] = valuePtr(m_port);
m_values[3] = valuePtr(m_user);
m_values[4] = valuePtr(m_password);
m_values[5] = valuePtr(m_dbname);
m_values[6] = valuePtr(m_sslMode);
m_values[7] = valuePtr(m_sslCert);
m_values[8] = valuePtr(m_sslKey);
m_values[9] = valuePtr(m_sslRootCert);
m_values[10] = valuePtr(m_sslCrl);
m_values[11] = "utf8"; m_values[11] = "utf8";
m_values[12] = valuePtr(m_applicationName); m_values[12] = valuePtr(m_applicationName);

View file

@ -66,7 +66,11 @@ ConnectionListModel::ConnectionListModel(QObject *parent)
int ConnectionListModel::rowCount(const QModelIndex &parent) const int ConnectionListModel::rowCount(const QModelIndex &parent) const
{ {
return m_connections.size(); int result = 0;
if (parent == QModelIndex()) {
result = m_connections.size();
}
return result;
} }
int ConnectionListModel::columnCount(const QModelIndex &/*parent*/) const int ConnectionListModel::columnCount(const QModelIndex &/*parent*/) const
@ -150,7 +154,12 @@ bool ConnectionListModel::setData(const QModelIndex &index, const QVariant &valu
Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const Qt::ItemFlags ConnectionListModel::flags(const QModelIndex &index) const
{ {
return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; Qt::ItemFlags result;
int row = index.row();
if (row >= 0 && row < m_connections.size()) {
result = Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled;
}
return result;
} }
@ -176,6 +185,28 @@ void ConnectionListModel::add(const ConnectionConfig &cfg)
emit dataChanged(idx, idx); emit dataChanged(idx, idx);
} }
Expected<ConnectionConfig> ConnectionListModel::get(int row)
{
if (row >= 0 && row < m_connections.size()) {
return m_connections.at(row).m_config;
}
else {
return Expected<ConnectionConfig>::fromException(std::out_of_range("Invalid row"));
}
}
//void ConnectionListModel::del(const int idx)
bool ConnectionListModel::removeRows(int row, int count, const QModelIndex &parent)
{
bool result = false;
if (row >= 0 && row < m_connections.size()) {
auto f = m_connections.begin() + row;
m_connections.erase(f, f + count);
result = true;
}
return result;
}
/// \todo should return an expected as creation of the folder can fail /// \todo should return an expected as creation of the folder can fail
QString ConnectionListModel::iniFileName() QString ConnectionListModel::iniFileName()
{ {

View file

@ -8,6 +8,7 @@
#include <QUuid> #include <QUuid>
#include "connectionconfig.h" #include "connectionconfig.h"
#include "expected.h"
class ConnectionListModel : public QAbstractListModel { class ConnectionListModel : public QAbstractListModel {
@ -23,9 +24,12 @@ public:
virtual Qt::ItemFlags flags(const QModelIndex &index) const override; virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
void add(const ConnectionConfig &cfg); void add(const ConnectionConfig &cfg);
Expected<ConnectionConfig> get(int row);
virtual bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override;
void load(); void load();
void save(); void save();
private: private:
class LijstElem { class LijstElem {
public: public:

View file

@ -1,6 +1,8 @@
#include "connectionmanagerwindow.h" #include "connectionmanagerwindow.h"
#include "ui_connectionmanagerwindow.h" #include "ui_connectionmanagerwindow.h"
#include "mainwindow.h"
#include <QDataWidgetMapper> #include <QDataWidgetMapper>
#include <QMessageBox>
#include <QStandardItemModel> #include <QStandardItemModel>
#include "connectionlistmodel.h" #include "connectionlistmodel.h"
@ -42,24 +44,15 @@ void ConnectionManagerWindow::on_currentChanged(const QModelIndex &current,
const QModelIndex &previous) const QModelIndex &previous)
{ {
int currow = current.row(); int currow = current.row();
// int prevrow = previous.row();
m_mapper->setCurrentIndex(currow); m_mapper->setCurrentIndex(currow);
// ui->lineEdit->setText(QString::number(currow));
// ui->lineEdit_2->setText(QString::number(prevrow));
// if(selection.indexes().isEmpty()) {
// clearMyView();
// } else {
// displayModelIndexInMyView(selection.indexes().first());
// }
} }
void ConnectionManagerWindow::on_actionDelete_connection_triggered() void ConnectionManagerWindow::on_actionDelete_connection_triggered()
{ {
auto ci = ui->listView->selectionModel()->currentIndex(); auto ci = ui->listView->selectionModel()->currentIndex();
if (ci.isValid()) { if (ci.isValid()) {
//m_listModel->removeRow(ci.row()); m_listModel->removeRow(ci.row());
} }
} }
@ -76,3 +69,27 @@ void ConnectionManagerWindow::setupWidgetMappings()
m_mapper->addMapping(ui->edtDbname, 6); m_mapper->addMapping(ui->edtDbname, 6);
m_mapper->toFirst(); m_mapper->toFirst();
} }
void ConnectionManagerWindow::on_actionConnect_triggered()
{
// Open a window for this connection, maybe we should first check the connection?
auto ci = ui->listView->selectionModel()->currentIndex();
auto cc = m_listModel->get(ci.row());
if (cc.valid()) {
auto w = new MainWindow;
w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(cc.get());
w->show();
}
}
void ConnectionManagerWindow::on_actionQuit_application_triggered()
{
auto res = QMessageBox::question(this, "pglab",
tr("Close ALL windows?"), QMessageBox::Yes, QMessageBox::No);
if (res == QMessageBox::Yes) {
QApplication::quit();
}
//closeAllWindows();
}

View file

@ -25,6 +25,10 @@ private slots:
void on_currentChanged(const QModelIndex &current, const QModelIndex &previous); void on_currentChanged(const QModelIndex &current, const QModelIndex &previous);
void on_actionDelete_connection_triggered(); void on_actionDelete_connection_triggered();
void on_actionConnect_triggered();
void on_actionQuit_application_triggered();
private: private:
Ui::ConnectionManagerWindow *ui; Ui::ConnectionManagerWindow *ui;
ConnectionListModel *m_listModel = nullptr; ConnectionListModel *m_listModel = nullptr;

View file

@ -197,6 +197,13 @@
<height>25</height> <height>25</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionQuit_application"/>
</widget>
<addaction name="menuFile"/>
</widget> </widget>
<widget class="QStatusBar" name="statusbar"/> <widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar"> <widget class="QToolBar" name="toolBar">
@ -250,6 +257,11 @@
<string>Connect</string> <string>Connect</string>
</property> </property>
</action> </action>
<action name="actionQuit_application">
<property name="text">
<string>Quit application</string>
</property>
</action>
</widget> </widget>
<resources> <resources>
<include location="resources.qrc"/> <include location="resources.qrc"/>

View file

@ -1,14 +1,103 @@
#include "databasewindow.h" #include "databasewindow.h"
#include "ui_databasewindow.h" #include "ui_databasewindow.h"
#include <QTimer>
DatabaseWindow::DatabaseWindow(QWidget *parent) : DatabaseWindow::DatabaseWindow(QWidget *parent) :
QMainWindow(parent), QMainWindow(parent),
ui(new Ui::DatabaseWindow) ui(new Ui::DatabaseWindow)
{ {
ui->setupUi(this); ui->setupUi(this);
m_dbConnection.setStateCallback([this](ASyncDBConnection::State st)
{
QueueTask([this, st]() { connectionStateChanged(st); });
});
m_dbConnection.setNoticeCallback([this](Pgsql::ErrorDetails details)
{
QueueTask([this, details]() { receiveNotice(details); });
});
} }
DatabaseWindow::~DatabaseWindow() DatabaseWindow::~DatabaseWindow()
{ {
m_dbConnection.closeConnection();
m_dbConnection.setStateCallback(nullptr);
delete ui; delete ui;
} }
void DatabaseWindow::setConfig(const ConnectionConfig &config)
{
m_config = config;
QString title = "pglab - ";
title += m_config.name().c_str();
setWindowTitle(title);
QueueTask([this]() { startConnect(); });
}
void DatabaseWindow::QueueTask(TSQueue::t_Callable c)
{
m_taskQueue.add(c);
// Theoretically this needs to be only called if the queue was empty because otherwise it already would
// be busy emptying the queue. For now however I think it is safer to call it just to make sure.
QMetaObject::invokeMethod(this, "processCallableQueue", Qt::QueuedConnection); // queues on main thread
}
void DatabaseWindow::processCallableQueue()
{
if (!m_taskQueue.empty()) {
auto c = m_taskQueue.pop();
c();
if (!m_taskQueue.empty()) {
QTimer::singleShot(0, this, SLOT(processCallableQueue()));
}
}
}
void DatabaseWindow::startConnect()
{
m_dbConnection.setupConnection(m_config);
}
void DatabaseWindow::connectionStateChanged(ASyncDBConnection::State state)
{
QString status_str;
switch (state) {
case ASyncDBConnection::State::NotConnected:
status_str = tr("Geen verbinding");
break;
case ASyncDBConnection::State::Connecting:
status_str = tr("Verbinden");
break;
case ASyncDBConnection::State::Connected:
status_str = tr("Verbonden");
break;
case ASyncDBConnection::State::QuerySend:
status_str = tr("Query verstuurd");
break;
case ASyncDBConnection::State::CancelSend:
status_str = tr("Query geannuleerd");
break;
}
// addLog(status_str);
statusBar()->showMessage(status_str);
}
void DatabaseWindow::receiveNotice(Pgsql::ErrorDetails notice)
{
// QTextCursor cursor = ui->messagesEdit->textCursor();
// cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
// QTextTable *table = cursor.insertTable(4, 2);
// if (table) {
// table->cellAt(1, 0).firstCursorPosition().insertText("State");
// table->cellAt(1, 1).firstCursorPosition().insertText(QString::fromStdString(notice.state));
// table->cellAt(2, 0).firstCursorPosition().insertText("Primary");
// table->cellAt(2, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messagePrimary));
// table->cellAt(3, 0).firstCursorPosition().insertText("Detail");
// table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail));
// }
}

View file

@ -1,8 +1,11 @@
#ifndef DATABASEWINDOW_H #ifndef DATABASEWINDOW_H
#define DATABASEWINDOW_H #define DATABASEWINDOW_H
#include "asyncdbconnection.h"
#include "tsqueue.h"
#include <QMainWindow> #include <QMainWindow>
namespace Ui { namespace Ui {
class DatabaseWindow; class DatabaseWindow;
} }
@ -15,8 +18,26 @@ public:
explicit DatabaseWindow(QWidget *parent = 0); explicit DatabaseWindow(QWidget *parent = 0);
~DatabaseWindow(); ~DatabaseWindow();
void setConfig(const ConnectionConfig &config);
/* Meant to be called from other threads to pass a code block
* that has to be executed in the context of the thread of the window.
*/
void QueueTask(TSQueue::t_Callable c);
private: private:
Ui::DatabaseWindow *ui; Ui::DatabaseWindow *ui;
TSQueue m_taskQueue;
ASyncDBConnection m_dbConnection;
ConnectionConfig m_config;
void connectionStateChanged(ASyncDBConnection::State state);
void receiveNotice(Pgsql::ErrorDetails notice);
void startConnect();
private slots:
void processCallableQueue();
}; };
#endif // DATABASEWINDOW_H #endif // DATABASEWINDOW_H

View file

@ -21,6 +21,11 @@
<attribute name="title"> <attribute name="title">
<string>Tab 1</string> <string>Tab 1</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QTableView" name="tableView"/>
</item>
</layout>
</widget> </widget>
<widget class="QWidget" name="tab_2"> <widget class="QWidget" name="tab_2">
<attribute name="title"> <attribute name="title">

View file

@ -4,77 +4,78 @@
template <typename T> template <typename T>
class Expected { class Expected {
union { union {
T ham; T m_value;
std::exception_ptr spam; std::exception_ptr m_error;
}; };
bool gotHam; bool m_valid;
Expected() {} // internal use Expected() {} // internal use
public: public:
Expected(const T& rhs) Expected(const T& rhs)
: ham(rhs), gotHam(true) : m_value(rhs), m_valid(true)
{} {}
Expected(T&& rhs) Expected(T&& rhs)
: ham(std::move(rhs)) : m_value(std::move(rhs))
, gotHam(true) , m_valid(true)
{} {}
Expected(const Expected& ths) Expected(const Expected& ths)
: gotHam(rhs.gotHam) : m_valid(rhs.valid)
{ {
if (gotHam) { if (m_valid) {
new (&ham) T(rhs.ham); new (&m_value) T(rhs.ham);
} }
else { else {
new (&spam) std::exception_ptr(rhs.spam); new (&m_error) std::exception_ptr(rhs.spam);
} }
} }
Expected(Expected &&rhs) Expected(Expected &&rhs)
: gotHam(rhs.getHam) : m_valid(rhs.m_valid)
{ {
if (gotHam) { if (m_valid) {
new (&ham) T(std::move(rhs.ham)); new (&m_value) T(std::move(rhs.m_value));
} }
else { else {
new (&spam) std::exception_ptr(std::move(rhs.spam)); new (&m_error) std::exception_ptr(std::move(rhs.m_error));
} }
} }
~Expected() ~Expected()
{ {
if (gotHam) { if (m_valid) {
ham.~T(); m_value.~T();
} }
else { else {
using std::exception_ptr; using std::exception_ptr;
spam.~exception_ptr(); m_error.~exception_ptr();
} }
} }
void swap(Expected& rhs) void swap(Expected& rhs)
{ {
if (gotHam) { if (m_valid) {
if (rhs.gotHam) { if (rhs.m_valid) {
using std::swamp; using std::swamp;
swap(ham, rhs.ham); swap(m_value, rhs.m_value);
} }
else { else {
auto t = std::move(rhs.spam); auto t = std::move(rhs.m_error);
new(&rhs.ham) T(std::move(ham)); new(&rhs.m_value) T(std::move(m_value));
new(&spam) std::exception_ptr(t); new(&m_error) std::exception_ptr(t);
std::swap(gotHam, rhs.getHam); std::swap(m_valid, rhs.getHam);
} }
} }
else { else {
if (rhs.gotHam) { if (rhs.m_valid) {
rhs.swap(*this); rhs.swap(*this);
} }
else { else {
spam.swap(rhs.spam); m_error.swap(rhs.m_error);
std::swap(gotHam, rhs.gotHam); std::swap(m_valid, rhs.m_valid);
} }
} }
} }
@ -91,8 +92,8 @@ public:
static Expected<T> fromException(std::exception_ptr p) static Expected<T> fromException(std::exception_ptr p)
{ {
Expected<T> result; Expected<T> result;
result.gotHam = false; result.m_valid = false;
new (&result.spam) std::exception_ptr(std::move(p)); new (&result.m_error) std::exception_ptr(std::move(p));
return result; return result;
} }
@ -103,31 +104,31 @@ public:
bool valid() const bool valid() const
{ {
return gotHam; return m_valid;
} }
T& get() T& get()
{ {
if (!gotHam) { if (!m_valid) {
std::rethrow_exception(spam); std::rethrow_exception(m_error);
} }
return ham; return m_value;
} }
const T& get() const const T& get() const
{ {
if (!gotHam) { if (!m_valid) {
std::rethrow_exception(spam); std::rethrow_exception(m_error);
} }
return ham; return m_value;
} }
template <class E> template <class E>
bool hasException() const bool hasException() const
{ {
try { try {
if (!gotHam) { if (!m_valid) {
std::rethrow_exception(spam); std::rethrow_exception(m_error);
} }
} }
catch (const E& object) { catch (const E& object) {

View file

@ -2,3 +2,12 @@
server_go.png=@server_go.png,0 server_go.png=@server_go.png,0
server_add.png=@server_add.png,0 server_add.png=@server_add.png,0
server_delete.png=@server_delete.png,0 server_delete.png=@server_delete.png,0
script_lightning.png=@script_lightning.png,0
script_delete.png=@script_delete.png,0
script_go.png=@script_go.png,0
database.png=@database.png,0
script.png=@script.png,0
folder.png=@folder.png,0
script_save.png=@script_save.png,0
lightbulb.png=@lightbulb.png,0
table_save.png=@table_save.png,0

BIN
icons/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
icons/lightbulb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
icons/script_delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
icons/script_go.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
icons/script_save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/table_save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -93,21 +93,18 @@ MainWindow::MainWindow(QWidget *parent)
font.setPointSize(10); font.setPointSize(10);
ui->queryEdit->setFont(font); ui->queryEdit->setFont(font);
highlighter.reset(new SqlHighlighter(ui->queryEdit->document())); highlighter.reset(new SqlHighlighter(ui->queryEdit->document()));
ui->queryEdit->setPlainText(test_query); // ui->queryEdit->setPlainText(test_query);
ui->connectionStringEdit->setText("user=postgres dbname=foutrapport password=admin"); // ui->connectionStringEdit->setText("user=postgres dbname=foutrapport password=admin");
QAction *action; // QAction *action;
action = ui->mainToolBar->addAction("connect"); // action = ui->mainToolBar->addAction("connect");
connect(action, &QAction::triggered, this, &MainWindow::startConnect); // connect(action, &QAction::triggered, this, &MainWindow::startConnect);
action = ui->mainToolBar->addAction("execute"); // action = ui->mainToolBar->addAction("explain");
connect(action, &QAction::triggered, this, &MainWindow::performQuery); // connect(action, &QAction::triggered, this, &MainWindow::performExplain);
action = ui->mainToolBar->addAction("explain"); // action = ui->mainToolBar->addAction("cancel");
connect(action, &QAction::triggered, this, &MainWindow::performExplain); // 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) m_dbConnection.setStateCallback([this](ASyncDBConnection::State st)
{ {
@ -130,6 +127,15 @@ MainWindow::~MainWindow()
delete ui; delete ui;
} }
void MainWindow::setConfig(const ConnectionConfig &config)
{
m_config = config;
QString title = "pglab - ";
title += m_config.name().c_str();
setWindowTitle(title);
QueueTask([this]() { startConnect(); });
}
void MainWindow::QueueTask(TSQueue::t_Callable c) void MainWindow::QueueTask(TSQueue::t_Callable c)
{ {
m_taskQueue.add(c); m_taskQueue.add(c);
@ -175,8 +181,9 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state)
void MainWindow::startConnect() void MainWindow::startConnect()
{ {
std::string connstr = ui->connectionStringEdit->text().toUtf8().data(); // std::string connstr = ui->connectionStringEdit->text().toUtf8().data();
m_dbConnection.setupConnection(connstr); // m_dbConnection.setupConnection(connstr);
m_dbConnection.setupConnection(m_config);
} }
void MainWindow::performQuery() void MainWindow::performQuery()
@ -206,6 +213,7 @@ void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
if (st == PGRES_TUPLES_OK) { if (st == PGRES_TUPLES_OK) {
resultModel.reset(new QueryResultModel(nullptr , dbres)); resultModel.reset(new QueryResultModel(nullptr , dbres));
ui->ResultView->setModel(resultModel.get()); ui->ResultView->setModel(resultModel.get());
ui->tabWidget->setCurrentWidget(ui->dataTab);
statusBar()->showMessage(tr("Query ready.")); statusBar()->showMessage(tr("Query ready."));
} }
else { else {
@ -239,6 +247,7 @@ void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
else { else {
statusBar()->showMessage(tr("No tuples returned, possibly an error...")); statusBar()->showMessage(tr("No tuples returned, possibly an error..."));
} }
ui->tabWidget->setCurrentWidget(ui->messageTab);
receiveNotice(dbres->diagDetails()); receiveNotice(dbres->diagDetails());
} }
} }
@ -303,10 +312,12 @@ void MainWindow::explain_ready(ExplainRoot::SPtr explain)
ui->explainTreeView->setColumnWidth(4, 80); ui->explainTreeView->setColumnWidth(4, 80);
ui->explainTreeView->setColumnWidth(5, 80); ui->explainTreeView->setColumnWidth(5, 80);
ui->explainTreeView->setColumnWidth(6, 600); ui->explainTreeView->setColumnWidth(6, 600);
ui->tabWidget->setCurrentWidget(ui->explainTab);
statusBar()->showMessage(tr("Explain ready.")); statusBar()->showMessage(tr("Explain ready."));
} }
else { else {
addLog("Explain no result"); addLog("Explain no result");
ui->tabWidget->setCurrentWidget(ui->messageTab);
statusBar()->showMessage(tr("Explain failed.")); statusBar()->showMessage(tr("Explain failed."));
} }
} }
@ -321,10 +332,6 @@ void MainWindow::receiveNotice(Pgsql::ErrorDetails notice)
QTextCursor cursor = ui->messagesEdit->textCursor(); QTextCursor cursor = ui->messagesEdit->textCursor();
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
// QString msg;
// cursor.insertText("TEST\r\n");
QTextTable *table = cursor.insertTable(4, 2); QTextTable *table = cursor.insertTable(4, 2);
if (table) { if (table) {
table->cellAt(1, 0).firstCursorPosition().insertText("State"); table->cellAt(1, 0).firstCursorPosition().insertText("State");
@ -466,3 +473,18 @@ void Copy( )
QApplication.clipboard().setText(selected_text); QApplication.clipboard().setText(selected_text);
} }
#endif #endif
void MainWindow::on_actionExecute_SQL_triggered()
{
performQuery();
}
void MainWindow::on_actionExplain_Analyze_triggered()
{
performExplain();
}
void MainWindow::on_actionCancel_triggered()
{
cancel_query();
}

View file

@ -2,6 +2,7 @@
#define MAINWINDOW_H #define MAINWINDOW_H
#include "asyncdbconnection.h" #include "asyncdbconnection.h"
#include "connectionconfig.h"
#include "tsqueue.h" #include "tsqueue.h"
#include <QLabel> #include <QLabel>
#include <QMainWindow> #include <QMainWindow>
@ -9,6 +10,8 @@
#include <memory> #include <memory>
#include <future> #include <future>
#include "PgsqlConn.h" #include "PgsqlConn.h"
#include <deque>
#include <mutex>
class ExplainRoot; class ExplainRoot;
class QueryResultModel; class QueryResultModel;
@ -20,28 +23,18 @@ namespace Ui {
} }
namespace Pgsql { namespace Pgsql {
class Connection; class Connection;
} }
#include <deque>
#include <mutex>
class TaskQueue {
public:
private:
};
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit MainWindow(QWidget *parent = 0); explicit MainWindow(QWidget *parent = 0);
~MainWindow(); ~MainWindow();
void setConfig(const ConnectionConfig &config);
/* Meant to be called from other threads to pass a code block /* Meant to be called from other threads to pass a code block
* that has to be executed in the context of the thread of the window. * that has to be executed in the context of the thread of the window.
*/ */
@ -52,6 +45,8 @@ private:
QLabel *m_timeElapsedLabel; QLabel *m_timeElapsedLabel;
std::unique_ptr<QTimer> m_timer; std::unique_ptr<QTimer> m_timer;
std::chrono::time_point<std::chrono::steady_clock> m_startTime; std::chrono::time_point<std::chrono::steady_clock> m_startTime;
ConnectionConfig m_config;
void startTimer(); void startTimer();
void endTimer(); void endTimer();
@ -88,6 +83,9 @@ private slots:
void on_actionExport_data_triggered(); void on_actionExport_data_triggered();
void on_actionClose_triggered(); void on_actionClose_triggered();
void on_actionAbout_triggered(); void on_actionAbout_triggered();
void on_actionExecute_SQL_triggered();
void on_actionExplain_Analyze_triggered();
void on_actionCancel_triggered();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View file

@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>MainWindow</string> <string>pglab - database</string>
</property> </property>
<widget class="QWidget" name="centralWidget"> <widget class="QWidget" name="centralWidget">
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
@ -27,9 +27,6 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>8</number> <number>8</number>
</property> </property>
<item>
<widget class="QLineEdit" name="connectionStringEdit"/>
</item>
<item> <item>
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
<property name="orientation"> <property name="orientation">
@ -211,19 +208,38 @@
<attribute name="toolBarBreak"> <attribute name="toolBarBreak">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
<addaction name="actionLoad_SQL"/>
<addaction name="actionSave_SQL"/>
<addaction name="actionExport_data"/>
<addaction name="separator"/>
<addaction name="actionExecute_SQL"/>
<addaction name="actionExplain_Analyze"/>
<addaction name="actionCancel"/>
</widget> </widget>
<widget class="QStatusBar" name="statusBar"/> <widget class="QStatusBar" name="statusBar"/>
<action name="actionLoad_SQL"> <action name="actionLoad_SQL">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/folder.png</normaloff>:/icons/folder.png</iconset>
</property>
<property name="text"> <property name="text">
<string>Load SQL</string> <string>Load SQL</string>
</property> </property>
</action> </action>
<action name="actionSave_SQL"> <action name="actionSave_SQL">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/script_save.png</normaloff>:/icons/script_save.png</iconset>
</property>
<property name="text"> <property name="text">
<string>Save SQL</string> <string>Save SQL</string>
</property> </property>
</action> </action>
<action name="actionExport_data"> <action name="actionExport_data">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/table_save.png</normaloff>:/icons/table_save.png</iconset>
</property>
<property name="text"> <property name="text">
<string>Export data</string> <string>Export data</string>
</property> </property>
@ -238,8 +254,39 @@
<string>About</string> <string>About</string>
</property> </property>
</action> </action>
<action name="actionExecute_SQL">
<property name="icon">
<iconset>
<normalon>:/icons/script_go.png</normalon>
</iconset>
</property>
<property name="text">
<string>Execute SQL</string>
</property>
</action>
<action name="actionCancel">
<property name="icon">
<iconset>
<normalon>:/icons/script_delete.png</normalon>
</iconset>
</property>
<property name="text">
<string>Cancel</string>
</property>
</action>
<action name="actionExplain_Analyze">
<property name="icon">
<iconset resource="resources.qrc">
<normaloff>:/icons/lightbulb.png</normaloff>:/icons/lightbulb.png</iconset>
</property>
<property name="text">
<string>Explain Analyze</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources/> <resources>
<include location="resources.qrc"/>
</resources>
<connections/> <connections/>
</ui> </ui>

View file

@ -1,7 +1,13 @@
<RCC> <RCC>
<qresource> <qresource prefix="/">
<file>icons/server_add.png</file> <file>icons/server_add.png</file>
<file>icons/server_delete.png</file> <file>icons/server_delete.png</file>
<file>icons/server_go.png</file> <file>icons/server_go.png</file>
<file>icons/script_delete.png</file>
<file>icons/script_go.png</file>
<file>icons/folder.png</file>
<file>icons/script_save.png</file>
<file>icons/lightbulb.png</file>
<file>icons/table_save.png</file>
</qresource> </qresource>
</RCC> </RCC>