Query tab will now show multiple data tabs if it get's multiple results

with tuples.
This commit is contained in:
Eelke Klein 2017-01-25 06:54:21 +01:00
parent 424cbc9e2e
commit b6d986051b
21 changed files with 349 additions and 113 deletions

View file

@ -250,6 +250,7 @@ void ASyncDBConnection::Thread::doNewCommand()
}
}
if (!command.empty() && m_connection.sendQuery(command)) {
m_timer.start();
doStateCallback(State::QuerySend);
}
else {
@ -290,9 +291,11 @@ void ASyncDBConnection::Thread::waitForResult()
while ( ! finished && ! m_connection.isBusy()) {
auto res(m_connection.getResult());
{
qint64 ms = m_timer.restart();
std::lock_guard<std::mutex> lg(m_commandQueue.m_mutex);
m_commandQueue.m_queue.front().on_result(res);
m_commandQueue.m_queue.front().on_result(res, ms);
if (res == nullptr) {
m_timer.invalidate();
m_commandQueue.m_queue.pop();
doStateCallback(State::Connected);
finished = true;

View file

@ -4,6 +4,7 @@
#include "PgsqlConn.h"
#include "win32event.h"
#include "connectionconfig.h"
#include <QElapsedTimer>
#include <functional>
#include <mutex>
#include <queue>
@ -21,7 +22,7 @@ public:
Terminating
};
using on_result_callback = std::function<void(std::shared_ptr<Pgsql::Result>)>;
using on_result_callback = std::function<void(std::shared_ptr<Pgsql::Result>, qint64)>;
using on_state_callback = std::function<void(State)>;
using on_notice_callback = std::function<void(Pgsql::ErrorDetails)>;
@ -102,6 +103,7 @@ private:
bool terminateRequested = false; ///< is set when the thread should stop
bool m_terminated = true;
Pgsql::Canceller m_canceller;
QElapsedTimer m_timer;
bool makeConnection();

View file

@ -0,0 +1,14 @@
#include "databaseinspectorwidget.h"
#include "ui_databaseinspectorwidget.h"
DatabaseInspectorWidget::DatabaseInspectorWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::DatabaseInspectorWidget)
{
ui->setupUi(this);
}
DatabaseInspectorWidget::~DatabaseInspectorWidget()
{
delete ui;
}

22
databaseinspectorwidget.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef DATABASEINSPECTORWIDGET_H
#define DATABASEINSPECTORWIDGET_H
#include <QWidget>
namespace Ui {
class DatabaseInspectorWidget;
}
class DatabaseInspectorWidget : public QWidget
{
Q_OBJECT
public:
explicit DatabaseInspectorWidget(QWidget *parent = 0);
~DatabaseInspectorWidget();
private:
Ui::DatabaseInspectorWidget *ui;
};
#endif // DATABASEINSPECTORWIDGET_H

View file

@ -1,13 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>DatabaseOverviewForm</class>
<widget class="QWidget" name="DatabaseOverviewForm">
<class>DatabaseInspectorWidget</class>
<widget class="QWidget" name="DatabaseInspectorWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>733</width>
<height>618</height>
<width>599</width>
<height>536</height>
</rect>
</property>
<property name="windowTitle">

View file

@ -1,14 +0,0 @@
#include "databaseoverviewform.h"
#include "ui_databaseoverviewform.h"
DatabaseOverviewForm::DatabaseOverviewForm(QWidget *parent) :
QWidget(parent),
ui(new Ui::DatabaseOverviewForm)
{
ui->setupUi(this);
}
DatabaseOverviewForm::~DatabaseOverviewForm()
{
delete ui;
}

View file

@ -11,3 +11,4 @@ folder.png=@folder.png,0
script_save.png=@script_save.png,0
lightbulb.png=@lightbulb.png,0
table_save.png=@table_save.png,0
lightbulb_off.png=@lightbulb_off.png,0

BIN
icons/information.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
icons/lightbulb_off.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
icons/page_white_delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -26,7 +26,7 @@ MainWindow::MainWindow(QWidget *parent)
ui->setupUi(this);
ui->tabWidget->setDocumentMode(true);
ui->tabWidget->setTabsClosable(true);
//ui->tabWidget->setTabsClosable(true);
}
MainWindow::~MainWindow()
@ -40,6 +40,7 @@ QueryTab* MainWindow::newSqlPage()
qt->setConfig(m_config);
ui->tabWidget->addTab(qt, "Tab");
ui->tabWidget->setCurrentWidget(qt);
qt->newdoc();
return qt;
}
@ -125,7 +126,8 @@ void MainWindow::on_actionExport_data_triggered()
void MainWindow::on_actionClose_triggered()
{
close();
//close();
on_tabWidget_tabCloseRequested(ui->tabWidget->currentIndex());
}
void MainWindow::on_actionAbout_triggered()
@ -172,11 +174,20 @@ void MainWindow::on_actionExecute_SQL_triggered()
}
}
void MainWindow::on_actionExplain_triggered()
{
QueryTab *tab = GetActiveQueryTab();
if (tab) {
tab->explain(false);
}
}
void MainWindow::on_actionExplain_Analyze_triggered()
{
QueryTab *tab = GetActiveQueryTab();
if (tab) {
tab->explainAnalyze();
tab->explain(true);
}
}
@ -221,3 +232,4 @@ void MainWindow::on_tabWidget_tabCloseRequested(int index)
ui->tabWidget->removeTab(index);
}
}

View file

@ -45,6 +45,7 @@ public:
private:
Ui::MainWindow *ui;
TSQueue m_taskQueue;
@ -56,6 +57,7 @@ private:
void closeEvent(QCloseEvent *event);
void showEvent(QShowEvent *event);
QueryTab *newSqlPage();
private slots:
void processCallableQueue();
@ -72,6 +74,7 @@ private slots:
void on_actionSave_copy_of_SQL_as_triggered();
void on_actionNew_SQL_triggered();
void on_tabWidget_tabCloseRequested(int index);
void on_actionExplain_triggered();
};
#endif // MAINWINDOW_H

View file

@ -45,7 +45,7 @@
<x>0</x>
<y>0</y>
<width>993</width>
<height>25</height>
<height>22</height>
</rect>
</property>
<widget class="QMenu" name="menuTest">
@ -67,7 +67,18 @@
</property>
<addaction name="actionAbout"/>
</widget>
<widget class="QMenu" name="menuQuery">
<property name="title">
<string>Query</string>
</property>
<addaction name="actionExecute_SQL"/>
<addaction name="actionExplain"/>
<addaction name="actionExplain_Analyze"/>
<addaction name="separator"/>
<addaction name="actionCancel"/>
</widget>
<addaction name="menuTest"/>
<addaction name="menuQuery"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QToolBar" name="mainToolBar">
@ -81,10 +92,14 @@
<addaction name="actionLoad_SQL"/>
<addaction name="actionSave_SQL"/>
<addaction name="actionExport_data"/>
<addaction name="actionClose"/>
<addaction name="separator"/>
<addaction name="actionExecute_SQL"/>
<addaction name="actionExplain"/>
<addaction name="actionExplain_Analyze"/>
<addaction name="actionCancel"/>
<addaction name="separator"/>
<addaction name="actionAbout"/>
</widget>
<widget class="QStatusBar" name="statusBar"/>
<action name="actionLoad_SQL">
@ -121,11 +136,24 @@
</property>
</action>
<action name="actionClose">
<property name="icon">
<iconset>
<normalon>:/icons/page_white_delete.png</normalon>
</iconset>
</property>
<property name="text">
<string>Close</string>
</property>
<property name="shortcut">
<string>Ctrl+F4</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset>
<normalon>:/icons/information.png</normalon>
</iconset>
</property>
<property name="text">
<string>About</string>
</property>
@ -137,7 +165,10 @@
</iconset>
</property>
<property name="text">
<string>Execute SQL</string>
<string>Execute queries</string>
</property>
<property name="toolTip">
<string>Execute the (selected) queries</string>
</property>
<property name="shortcut">
<string>F5</string>
@ -191,6 +222,22 @@
<string>Ctrl+N</string>
</property>
</action>
<action name="actionExplain">
<property name="icon">
<iconset>
<normalon>:/icons/lightbulb_off.png</normalon>
</iconset>
</property>
<property name="text">
<string>Explain</string>
</property>
<property name="toolTip">
<string>Explain the (selected) query</string>
</property>
<property name="shortcut">
<string>F7</string>
</property>
</action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources>

View file

@ -35,11 +35,15 @@ SOURCES += main.cpp\
connectionlistmodel.cpp \
connectionconfig.cpp \
backuprestore.cpp \
databaseoverviewform.cpp \
dbschema_database.cpp \
querytab.cpp \
stopwatch.cpp \
util.cpp
util.cpp \
databaseinspectorwidget.cpp \
pgtype.cpp \
pgsqldatabasecatalogue.cpp \
pgtypecontainer.cpp \
tuplesresultwidget.cpp
HEADERS += mainwindow.h \
serverproperties.h \
@ -60,17 +64,22 @@ HEADERS += mainwindow.h \
connectionconfig.h \
scopeguard.h \
expected.h \
databaseoverviewform.h \
dbschema_database.h \
querytab.h \
stopwatch.h \
util.h
util.h \
databaseinspectorwidget.h \
pgtype.h \
pgsqldatabasecatalogue.h \
pgtypecontainer.h \
tuplesresultwidget.h
FORMS += mainwindow.ui \
serverproperties.ui \
databasewindow.ui \
connectionmanagerwindow.ui \
databaseoverviewform.ui \
databaseinspectorwidget.ui \
tuplesresultwidget.ui \
querytab.ui
RESOURCES += \

View file

@ -40,9 +40,9 @@ QueryTab::QueryTab(MainWindow *win, QWidget *parent) :
highlighter.reset(new SqlHighlighter(ui->queryEdit->document()));
connect(ui->queryEdit, &QPlainTextEdit::textChanged, this, &QueryTab::queryTextChanged);
m_stopwatch.setOutputLabel(ui->lblElapsedTime);
ui->lblElapsedTime->clear();
ui->lblRowCount->clear();
// m_stopwatch.setOutputLabel(ui->lblElapsedTime);
// ui->lblElapsedTime->clear();
// ui->lblRowCount->clear();
}
QueryTab::~QueryTab()
@ -167,14 +167,14 @@ void QueryTab::execute()
std::string cmd = getCommand();
m_stopwatch.start();
m_dbConnection.send(cmd,
[this](std::shared_ptr<Pgsql::Result> res)
[this](std::shared_ptr<Pgsql::Result> res, qint64 elapsedms)
{
m_win->QueueTask([this, res]() { query_ready(res); });
m_win->QueueTask([this, res, elapsedms]() { query_ready(res, elapsedms); });
});
}
}
void QueryTab::explainAnalyze()
void QueryTab::explain(bool analyze)
{
ui->explainTreeView->setModel(nullptr);
explainModel.reset();
@ -182,10 +182,14 @@ void QueryTab::explainAnalyze()
addLog("Explain clicked");
std::string analyze_str;
if (analyze) {
analyze_str = "ANALYZE, ";
}
m_stopwatch.start();
std::string cmd = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) " + getCommand();
std::string cmd = "EXPLAIN (" + analyze_str + "VERBOSE, BUFFERS, FORMAT JSON) " + getCommand();
m_dbConnection.send(cmd,
[this](std::shared_ptr<Pgsql::Result> res)
[this](std::shared_ptr<Pgsql::Result> res, qint64 )
{
if (res) {
// Process explain data seperately
@ -216,7 +220,9 @@ void QueryTab::cancel()
void QueryTab::setFileName(const QString &filename)
{
m_fileName = filename;
setTabCaption(m_fileName);
QFileInfo fileInfo(filename);
QString fn(fileInfo.fileName());
setTabCaption(fn, m_fileName);
}
bool QueryTab::continueWithoutSavingWarning()
@ -403,7 +409,7 @@ std::string QueryTab::getCommand() const
return command.toUtf8().data();
}
void QueryTab::setTabCaption(const QString &caption)
void QueryTab::setTabCaption(const QString &caption, const QString &tooltip)
{
QWidget * w = parentWidget();
QWidget * p = w->parentWidget();
@ -412,32 +418,45 @@ void QueryTab::setTabCaption(const QString &caption)
int i = tabwidget->indexOf(this);
if (i >= 0) {
tabwidget->setTabText(i, caption);
tabwidget->setTabToolTip(i, tooltip);
}
}
}
void QueryTab::query_ready(std::shared_ptr<Pgsql::Result> dbres)
void QueryTab::query_ready(std::shared_ptr<Pgsql::Result> dbres, qint64 elapsedms)
{
m_stopwatch.stop();
if (dbres) {
addLog("query_ready with result");
auto st = dbres->resultStatus();
if (st == PGRES_TUPLES_OK) {
//int n_rows = dbres->getRows();
QString rowcount_str = QString("rows: %1").arg(dbres->getRows());
ui->lblRowCount->setText(rowcount_str);
resultModel.reset(new QueryResultModel(nullptr , dbres));
ui->ResultView->setModel(resultModel.get());
ui->tabWidget->setCurrentWidget(ui->dataTab);
//QString rowcount_str = QString("rows: %1").arg(dbres->getRows());
auto result_model = std::make_shared<QueryResultModel>(nullptr , dbres);
TuplesResultWidget *trw = new TuplesResultWidget;
trw->setResult(result_model, elapsedms);
resultList.push_back(trw);
ui->tabWidget->addTab(trw, "Data");
// ui->lblRowCount->setText(rowcount_str);
// resultModel.reset(new QueryResultModel(nullptr , dbres));
// ui->ResultView->setModel(resultModel.get());
// ui->tabWidget->setCurrentWidget(ui->dataTab);
//statusBar()->showMessage(tr("Query ready."));
}
else {
if (st == PGRES_COMMAND_OK) {
// statusBar()->showMessage(tr("Command OK."));
QString msg = tr("Query returned succesfully: %1 rows affected, %2 execution time.")
.arg(QString::number(dbres->tuplesAffected()))
.arg(m_stopwatch.elapsed()); //msfloatToHumanReadableString(elapsedTime.count()));
int tuples_affected = dbres->tuplesAffected();
QString msg;
if (tuples_affected >= 0)
msg = tr("Query returned succesfully: %1 rows affected, execution time %2")
.arg(QString::number(tuples_affected))
.arg(msfloatToHumanReadableString(elapsedms));
else
msg = tr("Query returned succesfully, execution time %1")
.arg(msfloatToHumanReadableString(elapsedms));
ui->messagesEdit->append(msg);
ui->tabWidget->setCurrentWidget(ui->messageTab);
@ -476,6 +495,7 @@ void QueryTab::query_ready(std::shared_ptr<Pgsql::Result> dbres)
}
}
else {
m_stopwatch.stop();
addLog("query_ready with NO result");
// statusBar()->showMessage(tr("Query cancelled."));
}
@ -483,7 +503,10 @@ void QueryTab::query_ready(std::shared_ptr<Pgsql::Result> dbres)
void QueryTab::clearResult()
{
ui->ResultView->setModel(nullptr);
resultModel.reset();
ui->lblRowCount->clear();
// ui->ResultView->setModel(nullptr);
// resultModel.reset();
for (auto e : resultList)
delete e;
resultList.clear();
// ui->lblRowCount->clear();
}

View file

@ -5,6 +5,7 @@
#include "queryresultmodel.h"
#include "queryexplainmodel.h"
#include "stopwatch.h"
#include "tuplesresultwidget.h"
#include <QWidget>
#include <memory>
@ -38,11 +39,22 @@ public:
void saveCopyAs();
void execute();
void explainAnalyze();
void explain(bool analyze);
void cancel();
bool canClose();
private:
// struct ResultTab {
// public:
// std::shared_ptr<QueryResultModel> resultModel;
// std::shared_ptr<TuplesResultWidget> tuplesResult;
//// ResultTab(std::shared_ptr<QueryResultModel> rm, Ui::TuplesResult *tr)
//// : resultModel(rm), tuplesResult(tr)
//// {}
// };
using ResultTabContainer = std::vector<TuplesResultWidget*>;
Ui::QueryTab *ui;
MainWindow *m_win;
std::unique_ptr<SqlHighlighter> highlighter;
@ -58,17 +70,20 @@ private:
QString promptUserForSaveSqlFilename();
ASyncDBConnection m_dbConnection;
std::unique_ptr<QueryResultModel> resultModel;
std::unique_ptr<QueryExplainModel> explainModel;
ResultTabContainer resultList;
void addLog(QString s);
std::string getCommand() const;
void explain_ready(ExplainRoot::SPtr explain);
void query_ready(std::shared_ptr<Pgsql::Result> dbres);
void query_ready(std::shared_ptr<Pgsql::Result> dbres, qint64 elapsedms);
void setTabCaption(const QString &caption);
void setTabCaption(const QString &caption, const QString &tooltip);
void clearResult();
private slots:

View file

@ -65,61 +65,6 @@
</item>
</layout>
</widget>
<widget class="QWidget" name="dataTab">
<attribute name="title">
<string>Data</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<widget class="QTableView" name="ResultView">
<property name="font">
<font>
<family>Source Sans Pro</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="verticalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="lblElapsedTime">
<property name="text">
<string>0ms</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblRowCount">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="explainTab">
<attribute name="title">
<string>Explain</string>

View file

@ -10,5 +10,8 @@
<file>icons/lightbulb.png</file>
<file>icons/table_save.png</file>
<file>icons/page_white_add.png</file>
<file>icons/page_white_delete.png</file>
<file>icons/lightbulb_off.png</file>
<file>icons/information.png</file>
</qresource>
</RCC>

26
tuplesresultwidget.cpp Normal file
View file

@ -0,0 +1,26 @@
#include "tuplesresultwidget.h"
#include "ui_tuplesresultwidget.h"
#include "util.h"
TuplesResultWidget::TuplesResultWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::TuplesResultWidget)
{
ui->setupUi(this);
ui->lblRowCount->setText(QString());
}
TuplesResultWidget::~TuplesResultWidget()
{
delete ui;
}
void TuplesResultWidget::setResult(std::shared_ptr<QueryResultModel> res, float ms)
{
resultModel = res;
ui->ResultView->setModel(resultModel.get());
QString rowcount_str = QString("rows: %1").arg(resultModel->rowCount());
ui->lblRowCount->setText(rowcount_str);
ui->lblElapsedTime->setText(msfloatToHumanReadableString(ms));
}

26
tuplesresultwidget.h Normal file
View file

@ -0,0 +1,26 @@
#ifndef TUPLESRESULTWIDGET_H
#define TUPLESRESULTWIDGET_H
#include "queryresultmodel.h"
#include <QWidget>
namespace Ui {
class TuplesResultWidget;
}
class TuplesResultWidget : public QWidget
{
Q_OBJECT
public:
explicit TuplesResultWidget(QWidget *parent = 0);
~TuplesResultWidget();
void setResult(std::shared_ptr<QueryResultModel> res, float ms);
private:
Ui::TuplesResultWidget *ui;
std::shared_ptr<QueryResultModel> resultModel;
};
#endif // TUPLESRESULTWIDGET_H

99
tuplesresultwidget.ui Normal file
View file

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TuplesResultWidget</class>
<widget class="QWidget" name="TuplesResultWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>4</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<widget class="QTableView" name="ResultView">
<property name="font">
<font>
<family>Source Sans Pro</family>
<pointsize>10</pointsize>
</font>
</property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="verticalHeaderDefaultSectionSize">
<number>20</number>
</attribute>
<attribute name="verticalHeaderMinimumSectionSize">
<number>16</number>
</attribute>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lblElapsedTime">
<property name="text">
<string>0ms</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblRowCount">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>