Show elapsed time during queries and show execute and plan time above query explain.

This commit is contained in:
Eelke Klein 2017-01-08 15:16:16 +01:00
parent 30638b11e5
commit 5f3ddb80c6
5 changed files with 194 additions and 7 deletions

View file

@ -12,7 +12,7 @@ TARGET = Ivory
TEMPLATE = app TEMPLATE = app
INCLUDEPATH += C:\prog\include INCLUDEPATH += C:\prog\include
DEFINES += WIN32_LEAN_AND_MEAN DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
LIBS += c:\prog\lib\libpq.lib User32.lib LIBS += c:\prog\lib\libpq.lib User32.lib
SOURCES += main.cpp\ SOURCES += main.cpp\

View file

@ -9,9 +9,61 @@
#include <windows.h> #include <windows.h>
#include "json/json.h" #include "json/json.h"
#include "explaintreemodelitem.h" #include "explaintreemodelitem.h"
#include <algorithm>
//#include <thread> //#include <thread>
namespace {
// Supported range from microseconds to seconds
// min:sec to hours::min::sec
QString msfloatToHumanReadableString(float ms)
{
QString unit;
float val;
int deci = 3;
if (ms < 1.0f) {
val = ms * 1000.f;
//result = QString::asprintf("%0.3f", ms * 1000.0f);
unit = u8"μs";
}
else if (ms >= 1000.0) {
val = ms / 1000.0f;
unit = "s";
if (val >= 60.0) {
int secs = val;
int min = secs / 60.0;
secs -= min * 60;
if (min >= 60) {
int hour = min / 60;
min -= hour * 60;
return QString::asprintf("%d:%02d:%02d", hour, min, secs);
}
else {
return QString::asprintf("%02d:%02d", min, secs);
}
}
}
else {
val = ms;
unit = "ms";
}
if (val >= 1000.f) {
deci = 0;
}
else if (val >= 100.f) {
deci = 1;
}
else if (val >= 10.f) {
deci = 2;
}
QString result = QString::asprintf("%0.*f", deci, val);
return result + unit;
}
}
namespace pg = Pgsql; namespace pg = Pgsql;
const char * test_query = const char * test_query =
@ -63,6 +115,9 @@ MainWindow::MainWindow(QWidget *parent)
{ {
QueueTask([this, details]() { receiveNotice(details); }); QueueTask([this, details]() { receiveNotice(details); });
}); });
m_timeElapsedLabel = new QLabel(this);
statusBar()->addPermanentWidget(m_timeElapsedLabel);
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -111,6 +166,7 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state)
status_str = tr("Query geannuleerd"); status_str = tr("Query geannuleerd");
break; break;
} }
addLog(status_str);
statusBar()->showMessage(status_str); statusBar()->showMessage(status_str);
} }
@ -122,12 +178,15 @@ void MainWindow::startConnect()
void MainWindow::performQuery() void MainWindow::performQuery()
{ {
addLog("Query clicked");
ui->ResultView->setModel(nullptr); ui->ResultView->setModel(nullptr);
resultModel.reset(); resultModel.reset();
ui->messagesEdit->clear(); ui->messagesEdit->clear();
QString command = ui->queryEdit->toPlainText(); QString command = ui->queryEdit->toPlainText();
std::string cmd = command.toUtf8().data(); std::string cmd = command.toUtf8().data();
startTimer();
m_dbConnection.send(cmd, m_dbConnection.send(cmd,
[this](std::shared_ptr<Pgsql::Result> res) [this](std::shared_ptr<Pgsql::Result> res)
{ {
@ -137,7 +196,9 @@ void MainWindow::performQuery()
void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres) void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
{ {
endTimer();
if (dbres) { if (dbres) {
addLog("query_ready with result");
auto st = dbres->resultStatus(); auto st = dbres->resultStatus();
if (st == PGRES_TUPLES_OK) { if (st == PGRES_TUPLES_OK) {
resultModel.reset(new QueryResultModel(nullptr , dbres)); resultModel.reset(new QueryResultModel(nullptr , dbres));
@ -179,16 +240,20 @@ void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
} }
} }
else { else {
addLog("query_ready with NO result");
statusBar()->showMessage(tr("Query cancelled.")); statusBar()->showMessage(tr("Query cancelled."));
} }
} }
void MainWindow::performExplain() void MainWindow::performExplain()
{ {
ui->ResultView->setModel(nullptr); ui->explainTreeView->setModel(nullptr);
resultModel.reset(); explainModel.reset();
ui->messagesEdit->clear(); ui->messagesEdit->clear();
addLog("Explain clicked");
startTimer();
QString command = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) "; QString command = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) ";
command += ui->queryEdit->toPlainText(); command += ui->queryEdit->toPlainText();
std::string cmd = command.toUtf8().data(); std::string cmd = command.toUtf8().data();
@ -217,7 +282,14 @@ void MainWindow::performExplain()
void MainWindow::explain_ready(ExplainRoot::SPtr explain) void MainWindow::explain_ready(ExplainRoot::SPtr explain)
{ {
endTimer();
if (explain) { if (explain) {
addLog("Explain ready");
QString times_str = QString("Execution time: %1, Planning time: %2")
.arg(
msfloatToHumanReadableString(explain->executionTime)
, msfloatToHumanReadableString(explain->planningTime));
ui->lblTimes->setText(times_str);
explainModel.reset(new QueryExplainModel(nullptr, explain)); explainModel.reset(new QueryExplainModel(nullptr, explain));
ui->explainTreeView->setModel(explainModel.get()); ui->explainTreeView->setModel(explainModel.get());
ui->explainTreeView->expandAll(); ui->explainTreeView->expandAll();
@ -231,6 +303,7 @@ void MainWindow::explain_ready(ExplainRoot::SPtr explain)
statusBar()->showMessage(tr("Explain ready.")); statusBar()->showMessage(tr("Explain ready."));
} }
else { else {
addLog("Explain no result");
statusBar()->showMessage(tr("Explain failed.")); statusBar()->showMessage(tr("Explain failed."));
} }
} }
@ -259,3 +332,41 @@ void MainWindow::receiveNotice(Pgsql::ErrorDetails notice)
table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail)); table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail));
} }
} }
void MainWindow::addLog(QString s)
{
QTextCursor text_cursor = QTextCursor(ui->edtLog->document());
text_cursor.movePosition(QTextCursor::End);
text_cursor.insertText(s + "\r\n");
}
void MainWindow::startTimer()
{
m_startTime = std::chrono::steady_clock::now();
m_timer = std::make_unique<QTimer>(nullptr);
m_timer->setTimerType(Qt::CoarseTimer);
connect(m_timer.get(), SIGNAL(timeout()), this, SLOT(updateTimer()));
m_timer->start(18);
}
void MainWindow::updateTimer()
{
auto nu = std::chrono::steady_clock::now();
std::chrono::duration<float, std::milli> diff = nu - m_startTime;
m_timeElapsedLabel->setText(msfloatToHumanReadableString(diff.count()));
if (m_timer) {
int interval = int(diff.count()) / 25;
interval = std::max(interval, 18);
interval = std::min(interval, 1000);
m_timer->start(interval);
}
}
void MainWindow::endTimer()
{
if (m_timer) {
m_timer.reset();
updateTimer();
}
}

View file

@ -3,6 +3,7 @@
#include "asyncdbconnection.h" #include "asyncdbconnection.h"
#include "tsqueue.h" #include "tsqueue.h"
#include <QLabel>
#include <QMainWindow> #include <QMainWindow>
#include <QSocketNotifier> #include <QSocketNotifier>
#include <memory> #include <memory>
@ -48,6 +49,12 @@ public:
private: private:
TSQueue m_taskQueue; TSQueue m_taskQueue;
QLabel *m_timeElapsedLabel;
std::unique_ptr<QTimer> m_timer;
std::chrono::time_point<std::chrono::steady_clock> m_startTime;
void startTimer();
void endTimer();
std::unique_ptr<Ui::MainWindow> ui; std::unique_ptr<Ui::MainWindow> ui;
@ -73,6 +80,9 @@ private slots:
void receiveNotice(Pgsql::ErrorDetails notice); void receiveNotice(Pgsql::ErrorDetails notice);
void processCallableQueue(); void processCallableQueue();
void addLog(QString s);
void updateTimer();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View file

@ -38,7 +38,7 @@
<widget class="QTextEdit" name="queryEdit"/> <widget class="QTextEdit" name="queryEdit"/>
<widget class="QTabWidget" name="tabWidget"> <widget class="QTabWidget" name="tabWidget">
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>2</number>
</property> </property>
<widget class="QWidget" name="messageTab"> <widget class="QWidget" name="messageTab">
<attribute name="title"> <attribute name="title">
@ -91,12 +91,24 @@
<pointsize>10</pointsize> <pointsize>10</pointsize>
</font> </font>
</property> </property>
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="verticalScrollMode"> <property name="verticalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
<property name="horizontalScrollMode"> <property name="horizontalScrollMode">
<enum>QAbstractItemView::ScrollPerPixel</enum> <enum>QAbstractItemView::ScrollPerPixel</enum>
</property> </property>
<property name="wordWrap">
<bool>false</bool>
</property>
<attribute name="verticalHeaderMinimumSectionSize">
<number>20</number>
</attribute>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -118,7 +130,7 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>2</number> <number>2</number>
</property> </property>
<item row="0" column="0"> <item row="1" column="0">
<widget class="QTreeView" name="explainTreeView"> <widget class="QTreeView" name="explainTreeView">
<property name="editTriggers"> <property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set> <set>QAbstractItemView::NoEditTriggers</set>
@ -126,14 +138,37 @@
<property name="showDropIndicator" stdset="0"> <property name="showDropIndicator" stdset="0">
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<property name="indentation"> <property name="indentation">
<number>10</number> <number>10</number>
</property> </property>
<property name="uniformRowHeights">
<bool>false</bool>
</property>
<attribute name="headerStretchLastSection"> <attribute name="headerStretchLastSection">
<bool>false</bool> <bool>false</bool>
</attribute> </attribute>
</widget> </widget>
</item> </item>
<item row="0" column="0">
<widget class="QLabel" name="lblTimes">
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="logTab">
<attribute name="title">
<string>Log</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPlainTextEdit" name="edtLog"/>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>

View file

@ -15,10 +15,12 @@
#include <QtWidgets/QButtonGroup> #include <QtWidgets/QButtonGroup>
#include <QtWidgets/QGridLayout> #include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView> #include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit> #include <QtWidgets/QLineEdit>
#include <QtWidgets/QMainWindow> #include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu> #include <QtWidgets/QMenu>
#include <QtWidgets/QMenuBar> #include <QtWidgets/QMenuBar>
#include <QtWidgets/QPlainTextEdit>
#include <QtWidgets/QSplitter> #include <QtWidgets/QSplitter>
#include <QtWidgets/QStatusBar> #include <QtWidgets/QStatusBar>
#include <QtWidgets/QTabWidget> #include <QtWidgets/QTabWidget>
@ -49,6 +51,10 @@ public:
QWidget *explainTab; QWidget *explainTab;
QGridLayout *gridLayout_3; QGridLayout *gridLayout_3;
QTreeView *explainTreeView; QTreeView *explainTreeView;
QLabel *lblTimes;
QWidget *logTab;
QVBoxLayout *verticalLayout_2;
QPlainTextEdit *edtLog;
QMenuBar *menuBar; QMenuBar *menuBar;
QMenu *menuTest; QMenu *menuTest;
QToolBar *mainToolBar; QToolBar *mainToolBar;
@ -106,8 +112,12 @@ public:
font.setFamily(QStringLiteral("Source Sans Pro")); font.setFamily(QStringLiteral("Source Sans Pro"));
font.setPointSize(10); font.setPointSize(10);
ResultView->setFont(font); ResultView->setFont(font);
ResultView->setEditTriggers(QAbstractItemView::NoEditTriggers);
ResultView->setAlternatingRowColors(true);
ResultView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ResultView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
ResultView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel); ResultView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
ResultView->setWordWrap(false);
ResultView->verticalHeader()->setMinimumSectionSize(20);
gridLayout->addWidget(ResultView, 0, 0, 1, 1); gridLayout->addWidget(ResultView, 0, 0, 1, 1);
@ -123,12 +133,31 @@ public:
explainTreeView->setObjectName(QStringLiteral("explainTreeView")); explainTreeView->setObjectName(QStringLiteral("explainTreeView"));
explainTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); explainTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
explainTreeView->setProperty("showDropIndicator", QVariant(false)); explainTreeView->setProperty("showDropIndicator", QVariant(false));
explainTreeView->setAlternatingRowColors(true);
explainTreeView->setIndentation(10); explainTreeView->setIndentation(10);
explainTreeView->setUniformRowHeights(false);
explainTreeView->header()->setStretchLastSection(false); explainTreeView->header()->setStretchLastSection(false);
gridLayout_3->addWidget(explainTreeView, 0, 0, 1, 1); gridLayout_3->addWidget(explainTreeView, 1, 0, 1, 1);
lblTimes = new QLabel(explainTab);
lblTimes->setObjectName(QStringLiteral("lblTimes"));
gridLayout_3->addWidget(lblTimes, 0, 0, 1, 1);
tabWidget->addTab(explainTab, QString()); tabWidget->addTab(explainTab, QString());
logTab = new QWidget();
logTab->setObjectName(QStringLiteral("logTab"));
verticalLayout_2 = new QVBoxLayout(logTab);
verticalLayout_2->setSpacing(6);
verticalLayout_2->setContentsMargins(11, 11, 11, 11);
verticalLayout_2->setObjectName(QStringLiteral("verticalLayout_2"));
edtLog = new QPlainTextEdit(logTab);
edtLog->setObjectName(QStringLiteral("edtLog"));
verticalLayout_2->addWidget(edtLog);
tabWidget->addTab(logTab, QString());
splitter->addWidget(tabWidget); splitter->addWidget(tabWidget);
verticalLayout->addWidget(splitter); verticalLayout->addWidget(splitter);
@ -151,7 +180,7 @@ public:
retranslateUi(MainWindow); retranslateUi(MainWindow);
tabWidget->setCurrentIndex(1); tabWidget->setCurrentIndex(2);
QMetaObject::connectSlotsByName(MainWindow); QMetaObject::connectSlotsByName(MainWindow);
@ -162,7 +191,9 @@ public:
MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR)); MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR));
tabWidget->setTabText(tabWidget->indexOf(messageTab), QApplication::translate("MainWindow", "Messages", Q_NULLPTR)); tabWidget->setTabText(tabWidget->indexOf(messageTab), QApplication::translate("MainWindow", "Messages", Q_NULLPTR));
tabWidget->setTabText(tabWidget->indexOf(dataTab), QApplication::translate("MainWindow", "Data", Q_NULLPTR)); tabWidget->setTabText(tabWidget->indexOf(dataTab), QApplication::translate("MainWindow", "Data", Q_NULLPTR));
lblTimes->setText(QString());
tabWidget->setTabText(tabWidget->indexOf(explainTab), QApplication::translate("MainWindow", "Explain", Q_NULLPTR)); tabWidget->setTabText(tabWidget->indexOf(explainTab), QApplication::translate("MainWindow", "Explain", Q_NULLPTR));
tabWidget->setTabText(tabWidget->indexOf(logTab), QApplication::translate("MainWindow", "Log", Q_NULLPTR));
menuTest->setTitle(QApplication::translate("MainWindow", "test", Q_NULLPTR)); menuTest->setTitle(QApplication::translate("MainWindow", "test", Q_NULLPTR));
} // retranslateUi } // retranslateUi