FIlename is now remembered and used by save. Save as added.

This commit is contained in:
Eelke Klein 2017-01-21 08:09:12 +01:00
parent 35932e7a8d
commit 69ac154b07
11 changed files with 539 additions and 169 deletions

View file

@ -21,7 +21,9 @@ namespace {
ErrorDetails ErrorDetails::createErrorDetailsFromPGresult(const PGresult *result) ErrorDetails ErrorDetails::createErrorDetailsFromPGresult(const PGresult *result)
{ {
ErrorDetails r; ErrorDetails r;
set_stdstring_with_charptr(r.errorMessage, PQresultErrorMessage(result));
set_stdstring_with_charptr(r.state, PQresultErrorField(result, PG_DIAG_SQLSTATE)); ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html set_stdstring_with_charptr(r.state, PQresultErrorField(result, PG_DIAG_SQLSTATE)); ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
set_stdstring_with_charptr(r.severity, PQresultErrorField(result, PG_DIAG_SEVERITY)); set_stdstring_with_charptr(r.severity, PQresultErrorField(result, PG_DIAG_SEVERITY));
set_stdstring_with_charptr(r.messagePrimary, PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY)); set_stdstring_with_charptr(r.messagePrimary, PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY));

View file

@ -46,6 +46,7 @@ namespace Pgsql {
public: public:
static ErrorDetails createErrorDetailsFromPGresult(const PGresult *res); static ErrorDetails createErrorDetailsFromPGresult(const PGresult *res);
std::string errorMessage;
std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
std::string severity; std::string severity;
std::string messagePrimary; std::string messagePrimary;

View file

@ -16,6 +16,7 @@
#include "explaintreemodelitem.h" #include "explaintreemodelitem.h"
#include <algorithm> #include <algorithm>
#include <QCloseEvent> #include <QCloseEvent>
#include <querytab.h>
//#include <thread> //#include <thread>
@ -88,6 +89,7 @@ const char * test_query =
MainWindow::MainWindow(QWidget *parent) MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent) : QMainWindow(parent)
, ui(new Ui::MainWindow) , ui(new Ui::MainWindow)
, m_queryTab(new QueryTab)
{ {
ui->setupUi(this); ui->setupUi(this);
@ -127,6 +129,15 @@ MainWindow::MainWindow(QWidget *parent)
m_timeElapsedLabel = new QLabel(this); m_timeElapsedLabel = new QLabel(this);
statusBar()->addPermanentWidget(m_timeElapsedLabel); statusBar()->addPermanentWidget(m_timeElapsedLabel);
{
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(m_queryTab);
layout->setMargin(4);
ui->tab_2->setLayout(layout);
}
} }
MainWindow::~MainWindow() MainWindow::~MainWindow()
@ -195,8 +206,6 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state)
void MainWindow::startConnect() void MainWindow::startConnect()
{ {
// std::string connstr = ui->connectionStringEdit->text().toUtf8().data();
// m_dbConnection.setupConnection(connstr);
m_dbConnection.setupConnection(m_config); m_dbConnection.setupConnection(m_config);
} }
@ -363,18 +372,49 @@ void MainWindow::cancel_query()
void MainWindow::receiveNotice(Pgsql::ErrorDetails notice) void MainWindow::receiveNotice(Pgsql::ErrorDetails notice)
{ {
QTextCursor cursor = ui->messagesEdit->textCursor(); ui->messagesEdit->append(QString::fromStdString(notice.errorMessage));
cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor);
QTextTable *table = cursor.insertTable(4, 2); ui->messagesEdit->append(QString::fromStdString(notice.severity));
if (table) { ui->messagesEdit->append(QString("At position: %1").arg(notice.statementPosition));
table->cellAt(1, 0).firstCursorPosition().insertText("State"); ui->messagesEdit->append(QString::fromStdString("State: " + notice.state));
table->cellAt(1, 1).firstCursorPosition().insertText(QString::fromStdString(notice.state)); ui->messagesEdit->append(QString::fromStdString("Primary: " + notice.messagePrimary));
table->cellAt(2, 0).firstCursorPosition().insertText("Primary"); ui->messagesEdit->append(QString::fromStdString("Detail: " + notice.messageDetail));
table->cellAt(2, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messagePrimary)); ui->messagesEdit->append(QString::fromStdString("Hint: " + notice.messageHint));
table->cellAt(3, 0).firstCursorPosition().insertText("Detail"); ui->messagesEdit->append(QString::fromStdString("Context: " + notice.context));
table->cellAt(3, 1).firstCursorPosition().insertText(QString::fromStdString(notice.messageDetail)); // std::string state; ///< PG_DIAG_SQLSTATE Error code as listed in https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
} // std::string severity;
// std::string messagePrimary;
// std::string messageDetail;
// std::string messageHint;
// int statementPosition; ///< First character is one, measured in characters not bytes!
// std::string context;
// int internalPosition;
// std::string internalQuery;
// std::string schemaName;
// std::string tableName;
// std::string columnName;
// std::string datatypeName;
// std::string constraintName;
// std::string sourceFile;
// std::string sourceLine;
// std::string sourceFunction;
// 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));
// }
// syntax error at or near "limit
// statementPosition
} }
void MainWindow::addLog(QString s) void MainWindow::addLog(QString s)
@ -455,19 +495,14 @@ void MainWindow::on_actionLoad_SQL_triggered()
ui->queryEdit->appendPlainText(line); ui->queryEdit->appendPlainText(line);
} }
m_queryTextChanged = false; m_queryTextChanged = false;
m_fileName = file_name;
} }
} }
} }
void MainWindow::on_actionSave_SQL_triggered() void MainWindow::saveSqlTo(const QString &filename)
{ {
QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory); QFile file(filename);
QString file_name = QFileDialog::getSaveFileName(this,
tr("Save query"), home_dir, tr("SQL file (*.sql)"));
if ( ! file_name.isEmpty()) {
QFile file(file_name);
if (file.open(QIODevice::ReadWrite)) { if (file.open(QIODevice::ReadWrite)) {
QTextStream stream(&file); QTextStream stream(&file);
QString text = ui->queryEdit->toPlainText(); QString text = ui->queryEdit->toPlainText();
@ -475,6 +510,26 @@ void MainWindow::on_actionSave_SQL_triggered()
m_queryTextChanged = false; m_queryTextChanged = false;
} }
} }
void MainWindow::on_actionSave_SQL_triggered()
{
if (m_fileName.isEmpty()) {
on_actionSave_SQL_as_triggered();
}
else {
saveSqlTo(m_fileName);
}
}
void MainWindow::on_actionSave_SQL_as_triggered()
{
QString home_dir = QStandardPaths::locate(QStandardPaths::HomeLocation, "", QStandardPaths::LocateDirectory);
QString file_name = QFileDialog::getSaveFileName(this,
tr("Save query"), home_dir, tr("SQL file (*.sql)"));
if ( ! file_name.isEmpty()) {
saveSqlTo(file_name);
m_fileName = file_name;
}
} }
void MainWindow::on_actionExport_data_triggered() void MainWindow::on_actionExport_data_triggered()
@ -555,3 +610,13 @@ void MainWindow::closeEvent(QCloseEvent *event)
event->ignore(); event->ignore();
} }
} }
void MainWindow::showEvent(QShowEvent *event)
{
if (!event->spontaneous()) {
m_queryTextChanged = false;
}
event->accept();
}

View file

@ -27,6 +27,8 @@ namespace Pgsql {
class Connection; class Connection;
} }
class QueryTab;
class QCloseEvent; class QCloseEvent;
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
@ -49,6 +51,7 @@ private:
std::chrono::time_point<std::chrono::steady_clock> m_startTime; std::chrono::time_point<std::chrono::steady_clock> m_startTime;
ConnectionConfig m_config; ConnectionConfig m_config;
bool m_queryTextChanged = false; bool m_queryTextChanged = false;
QString m_fileName;
void startTimer(); void startTimer();
@ -57,6 +60,7 @@ private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
QueryTab *m_queryTab = nullptr;
std::unique_ptr<SqlHighlighter> highlighter; std::unique_ptr<SqlHighlighter> highlighter;
ASyncDBConnection m_dbConnection; ASyncDBConnection m_dbConnection;
@ -72,6 +76,8 @@ private:
bool continueWithoutSaving(); bool continueWithoutSaving();
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);
void showEvent(QShowEvent *event);
void saveSqlTo(const QString &filename);
private slots: private slots:
void startConnect(); void startConnect();
@ -95,6 +101,7 @@ private slots:
void on_actionExecute_SQL_triggered(); void on_actionExecute_SQL_triggered();
void on_actionExplain_Analyze_triggered(); void on_actionExplain_Analyze_triggered();
void on_actionCancel_triggered(); void on_actionCancel_triggered();
void on_actionSave_SQL_as_triggered();
}; };
#endif // MAINWINDOW_H #endif // MAINWINDOW_H

View file

@ -6,26 +6,51 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>726</width> <width>993</width>
<height>589</height> <height>804</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>pglab - database</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_3">
<property name="spacing">
<number>7</number>
</property>
<property name="leftMargin"> <property name="leftMargin">
<number>8</number> <number>4</number>
</property> </property>
<property name="topMargin"> <property name="topMargin">
<number>8</number> <number>4</number>
</property> </property>
<property name="rightMargin"> <property name="rightMargin">
<number>8</number> <number>4</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>8</number> <number>4</number>
</property>
<item>
<widget class="QTabWidget" name="tabWidget_2">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>Tab 1</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property> </property>
<item> <item>
<widget class="QSplitter" name="splitter"> <widget class="QSplitter" name="splitter">
@ -173,12 +198,21 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>Tab 2</string>
</attribute>
</widget>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menuBar"> <widget class="QMenuBar" name="menuBar">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>726</width> <width>993</width>
<height>25</height> <height>25</height>
</rect> </rect>
</property> </property>
@ -188,6 +222,7 @@
</property> </property>
<addaction name="actionLoad_SQL"/> <addaction name="actionLoad_SQL"/>
<addaction name="actionSave_SQL"/> <addaction name="actionSave_SQL"/>
<addaction name="actionSave_SQL_as"/>
<addaction name="actionExport_data"/> <addaction name="actionExport_data"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionClose"/> <addaction name="actionClose"/>
@ -298,6 +333,11 @@
<string>Shift+F7</string> <string>Shift+F7</string>
</property> </property>
</action> </action>
<action name="actionSave_SQL_as">
<property name="text">
<string>Save SQL as</string>
</property>
</action>
</widget> </widget>
<layoutdefault spacing="6" margin="11"/> <layoutdefault spacing="6" margin="11"/>
<resources> <resources>

View file

@ -36,7 +36,8 @@ SOURCES += main.cpp\
connectionconfig.cpp \ connectionconfig.cpp \
backuprestore.cpp \ backuprestore.cpp \
databaseoverviewform.cpp \ databaseoverviewform.cpp \
dbschema_database.cpp dbschema_database.cpp \
querytab.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
serverproperties.h \ serverproperties.h \
@ -58,13 +59,15 @@ HEADERS += mainwindow.h \
scopeguard.h \ scopeguard.h \
expected.h \ expected.h \
databaseoverviewform.h \ databaseoverviewform.h \
dbschema_database.h dbschema_database.h \
querytab.h
FORMS += mainwindow.ui \ FORMS += mainwindow.ui \
serverproperties.ui \ serverproperties.ui \
databasewindow.ui \ databasewindow.ui \
connectionmanagerwindow.ui \ connectionmanagerwindow.ui \
databaseoverviewform.ui databaseoverviewform.ui \
querytab.ui
RESOURCES += \ RESOURCES += \
resources.qrc resources.qrc

View file

@ -54,6 +54,9 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const
s = (s == "t") ? "TRUE" : "FALSE"; s = (s == "t") ? "TRUE" : "FALSE";
// intentional fall through // intentional fall through
default: default:
if (s.length() > 256) {
s.truncate(256);
}
r = s; r = s;
} }

14
querytab.cpp Normal file
View file

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

22
querytab.h Normal file
View file

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

192
querytab.ui Normal file
View file

@ -0,0 +1,192 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QueryTab</class>
<widget class="QWidget" name="QueryTab">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>766</width>
<height>667</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QPlainTextEdit" name="queryEdit"/>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>3</number>
</property>
<widget class="QWidget" name="messageTab">
<attribute name="title">
<string>Messages</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_2">
<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 row="0" column="0">
<widget class="QTextEdit" name="messagesEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="dataTab">
<attribute name="title">
<string>Data</string>
</attribute>
<layout class="QGridLayout" name="gridLayout">
<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 row="0" column="0">
<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>
</layout>
</widget>
<widget class="QWidget" name="explainTab">
<attribute name="title">
<string>Explain</string>
</attribute>
<layout class="QGridLayout" name="gridLayout_3">
<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 row="1" column="0">
<widget class="QTreeView" name="explainTreeView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<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">
<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="QPlainTextEdit" name="edtLog"/>
</item>
</layout>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -11,6 +11,27 @@ static const wchar_t *operators[] = {
}; };
/*
+ - * / < > = ~ ! @ # % ^ & | ` ?
There are a few restrictions on your choice of name:
-- and /* cannot appear anywhere in an operator name, since they will be taken as the start of a comment.
A multicharacter operator name cannot end in + or -, unless the name also contains at least one of these characters:
~ ! @ # % ^ & | ` ?
For example, @- is an allowed operator name, but *- is not. This restriction allows PostgreSQL to parse SQL-compliant commands without requiring spaces between tokens.
The use of => as an operator name is deprecated. It may be disallowed altogether in a future release.
The operator != is mapped to <> on input, so these two names are always equivalent.
+ - * / < > =
*/
//static auto types = R"-(bigint|boolean|char|character varying|date|int[248]|integer|numeric|smallint|time|timestamp(?:tz)?|timestamp(?:\s+with\s+timezone)?|varchar)-"; //static auto types = R"-(bigint|boolean|char|character varying|date|int[248]|integer|numeric|smallint|time|timestamp(?:tz)?|timestamp(?:\s+with\s+timezone)?|varchar)-";
//static auto err = R"-(left|right|inner|outer)-"; //static auto err = R"-(left|right|inner|outer)-";