diff --git a/PgsqlConn.cpp b/PgsqlConn.cpp index a3e99e2..a5ad6e2 100644 --- a/PgsqlConn.cpp +++ b/PgsqlConn.cpp @@ -21,7 +21,9 @@ namespace { ErrorDetails ErrorDetails::createErrorDetailsFromPGresult(const PGresult *result) { + 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.severity, PQresultErrorField(result, PG_DIAG_SEVERITY)); set_stdstring_with_charptr(r.messagePrimary, PQresultErrorField(result, PG_DIAG_MESSAGE_PRIMARY)); diff --git a/PgsqlConn.h b/PgsqlConn.h index 4d982a1..e7d2e07 100644 --- a/PgsqlConn.h +++ b/PgsqlConn.h @@ -46,6 +46,7 @@ namespace Pgsql { public: 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 severity; std::string messagePrimary; diff --git a/mainwindow.cpp b/mainwindow.cpp index b87974d..374e964 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -16,6 +16,7 @@ #include "explaintreemodelitem.h" #include #include +#include //#include @@ -88,6 +89,7 @@ const char * test_query = MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) + , m_queryTab(new QueryTab) { ui->setupUi(this); @@ -127,6 +129,15 @@ MainWindow::MainWindow(QWidget *parent) m_timeElapsedLabel = new QLabel(this); statusBar()->addPermanentWidget(m_timeElapsedLabel); + + + { + QVBoxLayout *layout = new QVBoxLayout; + layout->addWidget(m_queryTab); + layout->setMargin(4); + ui->tab_2->setLayout(layout); + } + } MainWindow::~MainWindow() @@ -195,8 +206,6 @@ void MainWindow::connectionStateChanged(ASyncDBConnection::State state) void MainWindow::startConnect() { -// std::string connstr = ui->connectionStringEdit->text().toUtf8().data(); -// m_dbConnection.setupConnection(connstr); m_dbConnection.setupConnection(m_config); } @@ -363,18 +372,49 @@ void MainWindow::cancel_query() void MainWindow::receiveNotice(Pgsql::ErrorDetails notice) { - QTextCursor cursor = ui->messagesEdit->textCursor(); - cursor.movePosition(QTextCursor::End, QTextCursor::MoveAnchor); + ui->messagesEdit->append(QString::fromStdString(notice.errorMessage)); - 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)); - } + ui->messagesEdit->append(QString::fromStdString(notice.severity)); + ui->messagesEdit->append(QString("At position: %1").arg(notice.statementPosition)); + ui->messagesEdit->append(QString::fromStdString("State: " + notice.state)); + ui->messagesEdit->append(QString::fromStdString("Primary: " + notice.messagePrimary)); + ui->messagesEdit->append(QString::fromStdString("Detail: " + notice.messageDetail)); + ui->messagesEdit->append(QString::fromStdString("Hint: " + notice.messageHint)); + ui->messagesEdit->append(QString::fromStdString("Context: " + notice.context)); +// 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) @@ -455,25 +495,40 @@ void MainWindow::on_actionLoad_SQL_triggered() ui->queryEdit->appendPlainText(line); } m_queryTextChanged = false; + m_fileName = file_name; } + } +} - +void MainWindow::saveSqlTo(const QString &filename) +{ + QFile file(filename); + if (file.open(QIODevice::ReadWrite)) { + QTextStream stream(&file); + QString text = ui->queryEdit->toPlainText(); + stream << text; + 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()) { - QFile file(file_name); - if (file.open(QIODevice::ReadWrite)) { - QTextStream stream(&file); - QString text = ui->queryEdit->toPlainText(); - stream << text; - m_queryTextChanged = false; - } + saveSqlTo(file_name); + m_fileName = file_name; } } @@ -555,3 +610,13 @@ void MainWindow::closeEvent(QCloseEvent *event) event->ignore(); } } + +void MainWindow::showEvent(QShowEvent *event) +{ + if (!event->spontaneous()) { + m_queryTextChanged = false; + } + event->accept(); +} + + diff --git a/mainwindow.h b/mainwindow.h index 05dc4da..f194876 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -27,6 +27,8 @@ namespace Pgsql { class Connection; } +class QueryTab; + class QCloseEvent; class MainWindow : public QMainWindow @@ -49,6 +51,7 @@ private: std::chrono::time_point m_startTime; ConnectionConfig m_config; bool m_queryTextChanged = false; + QString m_fileName; void startTimer(); @@ -57,6 +60,7 @@ private: Ui::MainWindow *ui; + QueryTab *m_queryTab = nullptr; std::unique_ptr highlighter; ASyncDBConnection m_dbConnection; @@ -72,6 +76,8 @@ private: bool continueWithoutSaving(); void closeEvent(QCloseEvent *event); + void showEvent(QShowEvent *event); + void saveSqlTo(const QString &filename); private slots: void startConnect(); @@ -95,6 +101,7 @@ private slots: void on_actionExecute_SQL_triggered(); void on_actionExplain_Analyze_triggered(); void on_actionCancel_triggered(); + void on_actionSave_SQL_as_triggered(); }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index af68d7d..aad5b97 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -6,168 +6,202 @@ 0 0 - 726 - 589 + 993 + 804 pglab - database - + + + 7 + - 8 + 4 - 8 + 4 - 8 + 4 - 8 + 4 - - - Qt::Vertical + + + 0 - - - - 1 - - - - Messages - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - true + + + Tab 1 + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + Qt::Vertical + + + + + 1 + + + Messages + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + true + + + + + + + + Data + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + Source Sans Pro + 10 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + 20 + + + + + + + + Explain + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + 10 + + + false + + + false + + + + + + + + + + + + + + + Log + + + + + + + - - - - - - Data - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - Source Sans Pro - 10 - - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::ScrollPerPixel - - - QAbstractItemView::ScrollPerPixel - - - false - - - 20 - - - - - - - - Explain - - - - 2 - - - 2 - - - 2 - - - 2 - - - - - QAbstractItemView::NoEditTriggers - - - false - - - true - - - 10 - - - false - - - false - - - - - - - - - - - - - - - Log - - - - - - - + + + + + + + Tab 2 + @@ -178,7 +212,7 @@ 0 0 - 726 + 993 25 @@ -188,6 +222,7 @@ + @@ -298,6 +333,11 @@ Shift+F7 + + + Save SQL as + + diff --git a/pglab.pro b/pglab.pro index 57cb70b..4645c44 100644 --- a/pglab.pro +++ b/pglab.pro @@ -36,7 +36,8 @@ SOURCES += main.cpp\ connectionconfig.cpp \ backuprestore.cpp \ databaseoverviewform.cpp \ - dbschema_database.cpp + dbschema_database.cpp \ + querytab.cpp HEADERS += mainwindow.h \ serverproperties.h \ @@ -58,13 +59,15 @@ HEADERS += mainwindow.h \ scopeguard.h \ expected.h \ databaseoverviewform.h \ - dbschema_database.h + dbschema_database.h \ + querytab.h FORMS += mainwindow.ui \ serverproperties.ui \ databasewindow.ui \ connectionmanagerwindow.ui \ - databaseoverviewform.ui + databaseoverviewform.ui \ + querytab.ui RESOURCES += \ resources.qrc diff --git a/queryresultmodel.cpp b/queryresultmodel.cpp index 0b93d02..33732d7 100644 --- a/queryresultmodel.cpp +++ b/queryresultmodel.cpp @@ -54,6 +54,9 @@ QVariant QueryResultModel::data(const QModelIndex &index, int role) const s = (s == "t") ? "TRUE" : "FALSE"; // intentional fall through default: + if (s.length() > 256) { + s.truncate(256); + } r = s; } diff --git a/querytab.cpp b/querytab.cpp new file mode 100644 index 0000000..b92303e --- /dev/null +++ b/querytab.cpp @@ -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; +} diff --git a/querytab.h b/querytab.h new file mode 100644 index 0000000..68db91f --- /dev/null +++ b/querytab.h @@ -0,0 +1,22 @@ +#ifndef QUERYTAB_H +#define QUERYTAB_H + +#include + +namespace Ui { +class QueryTab; +} + +class QueryTab : public QWidget +{ + Q_OBJECT + +public: + explicit QueryTab(QWidget *parent = 0); + ~QueryTab(); + +private: + Ui::QueryTab *ui; +}; + +#endif // QUERYTAB_H diff --git a/querytab.ui b/querytab.ui new file mode 100644 index 0000000..ac61b75 --- /dev/null +++ b/querytab.ui @@ -0,0 +1,192 @@ + + + QueryTab + + + + 0 + 0 + 766 + 667 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + 3 + + + + Messages + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + true + + + + + + + + Data + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + Source Sans Pro + 10 + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::ScrollPerPixel + + + QAbstractItemView::ScrollPerPixel + + + false + + + 20 + + + + + + + + Explain + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + 10 + + + false + + + false + + + + + + + + + + + + + + + Log + + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + + + + + + + + + diff --git a/sqlhighlighter.cpp b/sqlhighlighter.cpp index 565c07d..575dddc 100644 --- a/sqlhighlighter.cpp +++ b/sqlhighlighter.cpp @@ -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 err = R"-(left|right|inner|outer)-";