Show elapsed time during queries and show execute and plan time above query explain.
This commit is contained in:
parent
30638b11e5
commit
5f3ddb80c6
5 changed files with 194 additions and 7 deletions
|
|
@ -12,7 +12,7 @@ TARGET = Ivory
|
|||
TEMPLATE = app
|
||||
|
||||
INCLUDEPATH += C:\prog\include
|
||||
DEFINES += WIN32_LEAN_AND_MEAN
|
||||
DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX
|
||||
LIBS += c:\prog\lib\libpq.lib User32.lib
|
||||
|
||||
SOURCES += main.cpp\
|
||||
|
|
|
|||
115
mainwindow.cpp
115
mainwindow.cpp
|
|
@ -9,9 +9,61 @@
|
|||
#include <windows.h>
|
||||
#include "json/json.h"
|
||||
#include "explaintreemodelitem.h"
|
||||
#include <algorithm>
|
||||
|
||||
//#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;
|
||||
|
||||
const char * test_query =
|
||||
|
|
@ -63,6 +115,9 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
{
|
||||
QueueTask([this, details]() { receiveNotice(details); });
|
||||
});
|
||||
|
||||
m_timeElapsedLabel = new QLabel(this);
|
||||
statusBar()->addPermanentWidget(m_timeElapsedLabel);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
|
@ -111,6 +166,7 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state)
|
|||
status_str = tr("Query geannuleerd");
|
||||
break;
|
||||
}
|
||||
addLog(status_str);
|
||||
statusBar()->showMessage(status_str);
|
||||
}
|
||||
|
||||
|
|
@ -122,12 +178,15 @@ void MainWindow::startConnect()
|
|||
|
||||
void MainWindow::performQuery()
|
||||
{
|
||||
addLog("Query clicked");
|
||||
|
||||
ui->ResultView->setModel(nullptr);
|
||||
resultModel.reset();
|
||||
ui->messagesEdit->clear();
|
||||
|
||||
QString command = ui->queryEdit->toPlainText();
|
||||
std::string cmd = command.toUtf8().data();
|
||||
startTimer();
|
||||
m_dbConnection.send(cmd,
|
||||
[this](std::shared_ptr<Pgsql::Result> res)
|
||||
{
|
||||
|
|
@ -137,7 +196,9 @@ void MainWindow::performQuery()
|
|||
|
||||
void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
|
||||
{
|
||||
endTimer();
|
||||
if (dbres) {
|
||||
addLog("query_ready with result");
|
||||
auto st = dbres->resultStatus();
|
||||
if (st == PGRES_TUPLES_OK) {
|
||||
resultModel.reset(new QueryResultModel(nullptr , dbres));
|
||||
|
|
@ -179,16 +240,20 @@ void MainWindow::query_ready(std::shared_ptr<Pgsql::Result> dbres)
|
|||
}
|
||||
}
|
||||
else {
|
||||
addLog("query_ready with NO result");
|
||||
statusBar()->showMessage(tr("Query cancelled."));
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::performExplain()
|
||||
{
|
||||
ui->ResultView->setModel(nullptr);
|
||||
resultModel.reset();
|
||||
ui->explainTreeView->setModel(nullptr);
|
||||
explainModel.reset();
|
||||
ui->messagesEdit->clear();
|
||||
|
||||
addLog("Explain clicked");
|
||||
|
||||
startTimer();
|
||||
QString command = "EXPLAIN (ANALYZE, VERBOSE, BUFFERS, FORMAT JSON) ";
|
||||
command += ui->queryEdit->toPlainText();
|
||||
std::string cmd = command.toUtf8().data();
|
||||
|
|
@ -217,7 +282,14 @@ void MainWindow::performExplain()
|
|||
|
||||
void MainWindow::explain_ready(ExplainRoot::SPtr explain)
|
||||
{
|
||||
endTimer();
|
||||
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));
|
||||
ui->explainTreeView->setModel(explainModel.get());
|
||||
ui->explainTreeView->expandAll();
|
||||
|
|
@ -231,6 +303,7 @@ void MainWindow::explain_ready(ExplainRoot::SPtr explain)
|
|||
statusBar()->showMessage(tr("Explain ready."));
|
||||
}
|
||||
else {
|
||||
addLog("Explain no result");
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
10
mainwindow.h
10
mainwindow.h
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include "asyncdbconnection.h"
|
||||
#include "tsqueue.h"
|
||||
#include <QLabel>
|
||||
#include <QMainWindow>
|
||||
#include <QSocketNotifier>
|
||||
#include <memory>
|
||||
|
|
@ -48,6 +49,12 @@ public:
|
|||
|
||||
private:
|
||||
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;
|
||||
|
|
@ -73,6 +80,9 @@ private slots:
|
|||
void receiveNotice(Pgsql::ErrorDetails notice);
|
||||
|
||||
void processCallableQueue();
|
||||
|
||||
void addLog(QString s);
|
||||
void updateTimer();
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
<widget class="QTextEdit" name="queryEdit"/>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>2</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="messageTab">
|
||||
<attribute name="title">
|
||||
|
|
@ -91,12 +91,24 @@
|
|||
<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>
|
||||
</layout>
|
||||
|
|
@ -118,7 +130,7 @@
|
|||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<item row="1" column="0">
|
||||
<widget class="QTreeView" name="explainTreeView">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
|
|
@ -126,14 +138,37 @@
|
|||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="indentation">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<property name="uniformRowHeights">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</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>
|
||||
</widget>
|
||||
</widget>
|
||||
|
|
|
|||
|
|
@ -15,10 +15,12 @@
|
|||
#include <QtWidgets/QButtonGroup>
|
||||
#include <QtWidgets/QGridLayout>
|
||||
#include <QtWidgets/QHeaderView>
|
||||
#include <QtWidgets/QLabel>
|
||||
#include <QtWidgets/QLineEdit>
|
||||
#include <QtWidgets/QMainWindow>
|
||||
#include <QtWidgets/QMenu>
|
||||
#include <QtWidgets/QMenuBar>
|
||||
#include <QtWidgets/QPlainTextEdit>
|
||||
#include <QtWidgets/QSplitter>
|
||||
#include <QtWidgets/QStatusBar>
|
||||
#include <QtWidgets/QTabWidget>
|
||||
|
|
@ -49,6 +51,10 @@ public:
|
|||
QWidget *explainTab;
|
||||
QGridLayout *gridLayout_3;
|
||||
QTreeView *explainTreeView;
|
||||
QLabel *lblTimes;
|
||||
QWidget *logTab;
|
||||
QVBoxLayout *verticalLayout_2;
|
||||
QPlainTextEdit *edtLog;
|
||||
QMenuBar *menuBar;
|
||||
QMenu *menuTest;
|
||||
QToolBar *mainToolBar;
|
||||
|
|
@ -106,8 +112,12 @@ public:
|
|||
font.setFamily(QStringLiteral("Source Sans Pro"));
|
||||
font.setPointSize(10);
|
||||
ResultView->setFont(font);
|
||||
ResultView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
ResultView->setAlternatingRowColors(true);
|
||||
ResultView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
ResultView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
ResultView->setWordWrap(false);
|
||||
ResultView->verticalHeader()->setMinimumSectionSize(20);
|
||||
|
||||
gridLayout->addWidget(ResultView, 0, 0, 1, 1);
|
||||
|
||||
|
|
@ -123,12 +133,31 @@ public:
|
|||
explainTreeView->setObjectName(QStringLiteral("explainTreeView"));
|
||||
explainTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
explainTreeView->setProperty("showDropIndicator", QVariant(false));
|
||||
explainTreeView->setAlternatingRowColors(true);
|
||||
explainTreeView->setIndentation(10);
|
||||
explainTreeView->setUniformRowHeights(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());
|
||||
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);
|
||||
|
||||
verticalLayout->addWidget(splitter);
|
||||
|
|
@ -151,7 +180,7 @@ public:
|
|||
|
||||
retranslateUi(MainWindow);
|
||||
|
||||
tabWidget->setCurrentIndex(1);
|
||||
tabWidget->setCurrentIndex(2);
|
||||
|
||||
|
||||
QMetaObject::connectSlotsByName(MainWindow);
|
||||
|
|
@ -162,7 +191,9 @@ public:
|
|||
MainWindow->setWindowTitle(QApplication::translate("MainWindow", "MainWindow", Q_NULLPTR));
|
||||
tabWidget->setTabText(tabWidget->indexOf(messageTab), QApplication::translate("MainWindow", "Messages", 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(logTab), QApplication::translate("MainWindow", "Log", Q_NULLPTR));
|
||||
menuTest->setTitle(QApplication::translate("MainWindow", "test", Q_NULLPTR));
|
||||
} // retranslateUi
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue