Compare commits

...

65 commits
0.1.0 ... main

Author SHA1 Message Date
eelke
cf7c0699fe Remove double base64 encoding on storage 2025-04-01 20:27:58 +02:00
eelke
581e92723d Fix problems with reconnects where dialog keeps popping up. 2025-04-01 20:26:44 +02:00
eelke
a6be979f8e Allow for saving the configuration used by a window opened from a URL.
Note when editing a copy save password works now too.
2025-03-10 19:08:02 +01:00
eelke
05e9b982cd Fix explain colors in dark mode 2025-03-10 19:01:03 +01:00
eelke
2e516c9284 Fix verwijderen connectie 2025-03-10 19:00:47 +01:00
eelke
fdf049a2a3 Fix opslaan van connectie 2025-02-25 18:26:05 +01:00
eelke
3c943b1503 Improved disconnect handling in ASyncDBConnectionThread 2025-02-25 17:55:53 +01:00
eelke
a5069bb2b9 Fix that an URL without a port resulted in wrong portnumber being used. Closes #75 2025-02-24 18:48:19 +01:00
eelke
7dc665948d Activate incomplete namespaces tab 2025-02-23 16:53:45 +01:00
eelke
daed59e51a Fix using savedPasswords did not work. 2025-02-23 16:53:15 +01:00
eelke
ee86327869 Fix warnings and dependencies 2025-02-23 16:52:39 +01:00
eelke
86a9a0d709 Dark mode support
Centralized all colors, tweaked application paletter in darkmode to make it darker.
2025-02-23 15:38:49 +01:00
eelke
aac55b0ed1 Store connection configuration as key value pairs
Add migration for the sqlite database.
Because the Qt SQL library is a bit hard to work with use sqlite through custom wrapper.
2025-02-22 19:59:24 +01:00
eelke
4caccf1000 Basic support for passing postgresql uri on the commandline 2025-02-17 18:09:19 +01:00
eelke
4b4c95e57e Fix couple of warnings 2024-04-13 09:10:15 +02:00
eelke
80f751aeea Pgsql lib add some convenience routines 2024-04-13 09:09:54 +02:00
eelke
39927bbadf Turn PgAttribute::Key into class instead of alias for a std::tuple.
This improves readability as the fields are now named instead of numbered.
2024-04-13 09:06:51 +02:00
eelke
5bdd3fa95d Improve conversion of bytes to human readable string
Fixes issues like showing 0 MiB when the value is just slightly less then 1 MiB.
2024-04-12 13:26:10 +02:00
eelke
db2594a87c Make enum DataMeaning an enum class 2024-04-12 06:29:57 +02:00
eelke
dc97fa63e1 Fix compile error after Qt upgrade 2024-04-12 06:27:23 +02:00
eelke
d22b125d81 Accessing Scrypt implementation directly is deprecated. 2024-04-12 06:26:54 +02:00
eelke
e5a5dc15f9 Fix that CRUD window did not properly quote column names when this is required. 2023-10-18 17:50:44 +02:00
eelke
3cc28231f9 Support expressions as partitioning keys 2023-03-21 16:20:25 +01:00
eelke
2c899bd799 Generate PARTITIONED BY SQL for partitioned tables.
Expressions not yet supported.
2023-02-06 20:31:00 +01:00
eelke
61f90668d8 Fix DROP sql for partitioned table. 2023-01-30 20:08:24 +01:00
Eelke Klein
2ff9577d41 The table inheritance works mostly 2023-01-24 17:47:52 +00:00
eelke
ccd88d0578 Show in constraint list and in generated SQL when a constraint is inherited 2023-01-21 10:27:17 +01:00
eelke
60fb4ce285 Improve support for declarative partitioning.
Generated SQL for a partition is now correct (atleast for simple cases)
Switched to C++ 20 so the ranges library can be used in this case
to filter unwanted items.
2023-01-18 19:43:12 +01:00
eelke
7a4d8f3410 Release note for $$ fix 2023-01-08 11:20:19 +01:00
eelke
33319e3461 Fix reading from catalog so that information about declarative partitioning is read correctly
(View and SQL generation still need fixes)
2023-01-07 07:44:33 +01:00
eelke
0cd019db92 Fix for $-quoted strings
Note this does not solve all issues because we are tokenizing contents of strings of which we do not know they contains SQL
when the string is actually not SQL and contains $ the tokenizer gets confused.
2023-01-07 07:41:58 +01:00
eelke
f3f1d47f7d connection details in window title 2022-10-02 12:20:36 +02:00
eelke
f253a6c9ff Move version string into Config header.
Makes it easier to update the version number.
2022-10-02 12:11:43 +02:00
eelke
e68f17432c the job for gitlab pages must be called pages 2022-09-20 19:23:25 +02:00
eelke
7c88716ab7 Fix links 2022-09-20 19:15:28 +02:00
eelke
0911d2471e Document fix from 9277731c 2022-09-08 09:49:17 +02:00
eelke
92f924f738 documentation 2022-09-08 09:42:51 +02:00
eelke
c942d0a446 More documentation 2022-09-06 13:19:09 +02:00
Eelke Klein
50c45426d9 Merge branch '71-generated-support' into 'main'
Resolve "Improve GENERATED support"

Closes #71

See merge request eelke/pgLab!11
2022-09-06 11:17:19 +00:00
Eelke Klein
9277731c4e Resolve "Improve GENERATED support" 2022-09-06 11:17:18 +00:00
Eelke Klein
54e39ccdb3 Add README.md 2022-09-05 18:14:58 +00:00
Eelke Klein
5b85efd8df Deleted readme.md 2022-09-05 18:08:45 +00:00
eelke
21dd9fd930 Fix furo version 2022-09-05 15:41:02 +02:00
eelke
0483dc8b3d Improved releasenotes manual can also be opened from a databasewindow now. 2022-09-05 15:01:33 +02:00
eelke
487f84cf95 Update docs and improve releasenotes 2022-09-05 14:52:24 +02:00
eelke
da19c46d5e Improve editing of connection password
Previously only a new password was saved if the save password checkbox was checked, Which always
started in the unchecked state. Now when editing existing connection the save password checkbox now
reflects if a password has been saved. Only when the password field is edited the program will update
the saved password. If the save password checkbox is unchecked then clear the save password.
2022-09-05 14:35:45 +02:00
eelke
677302b5a7 Bind F5 to curd and catalog refresh actions 2022-09-05 13:24:39 +02:00
eelke
90d9c1109d Cleanup old files 2022-09-05 08:25:58 +02:00
eelke
eac308c5b1 Fix backslashes need escaping. 2022-09-05 08:14:18 +02:00
eelke
38290145c8 switch documentation to furo theme
Because cleaner look, more useful contents sidebar and light/dark mode support.
2022-09-05 08:14:01 +02:00
eelke
df24564d6b fix save connection password on uninitialized manager did not save
it did initialize the manager
2022-09-05 08:02:00 +02:00
eelke
3f41fc07c3 Was not saved properly 2022-09-05 07:45:59 +02:00
eelke
4fa2189b27 Added the capability to reset the password manager
Also some documentation about the password manager.
2022-09-05 07:33:08 +02:00
eelke
f8528143ac these items were not really features 2022-09-04 13:32:38 +02:00
eelke
c748b267a6 fix pipeline
installing python packages without gcc seems to be very hard
also fix pip caching
2022-09-04 13:28:08 +02:00
eelke
f3025928a3 fix pipeline needing gcc for dulwich 2022-09-04 13:00:58 +02:00
eelke
6b8c54398d Add installation and releasenotes sections to the user manual. 2022-09-04 12:43:09 +02:00
eelke
61346ae95d Remove BUILD because its contents is completely out of date. 2022-09-04 12:41:37 +02:00
Eelke Klein
6631d1df5d fix building of docs 2022-09-04 07:43:35 +00:00
Eelke Klein
a8009a1c37 Change docs theme 2022-09-04 07:40:14 +00:00
eelke
1d53a6397b There is no master branch it is called main. 2022-09-04 09:26:35 +02:00
eelke
c874b297c1 Add about dialog to File menu of the connection manager.
Make the contents of the dialog more accurate and include a link to the releasenotes file.
2022-09-04 09:19:03 +02:00
eelke
4e1d128ee9 fix header include path 2022-09-03 12:43:16 +02:00
eelke
f8f06232b9 Start using reno 2022-09-03 09:41:12 +02:00
eelke
bb0e08461a cleanup 2022-09-03 09:36:04 +02:00
157 changed files with 279892 additions and 1899 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ srcdoc/
pglabAll.pro.user.4.8-pre1
*.user
/pglabAll.pro.user*
/public/

View file

@ -5,26 +5,35 @@ variables:
cache:
paths:
- "$CI_PROJECT_DIR/pip-cache"
- "$PIP_CACHE_DIR"
key: "$CI_PROJECT_ID"
.install_sphinx:
before_script:
- apk add gcc musl-dev
- pip install --cache-dir "$PIP_CACHE_DIR" -U -r docs/requirements.txt
test-docs:
stage: test
extends:
- .install_sphinx
script:
- pip install -U sphinx
- sphinx-build -b html docs public
- sphinx-build -b html docs userdocs
artifacts:
paths:
- userdocs
only:
- branches
except:
- master
pages:
stage: deploy
extends:
- .install_sphinx
script:
- pip install -U sphinx
- sphinx-build -b html docs public
artifacts:
paths:
- public
only:
- master
- tags

18
BUILD
View file

@ -1,18 +0,0 @@
If it doesn't detect Qt CMAKE_PREFIX_PATH needs to be set
export CMAKE_PREFIX_PATH=/usr/lib/x86_64-linux-gnu/qt5/mkspecs/features/data/cmake
Dependencies:
- boost (asio, system, ???)
- Botan-2
- libpq
- Qt5
- fmt (should we include this one in the project?
- jsoncpp (included)
On ubuntu
sudo apt install libpq-dev libboost1.63-all-dev

13
README.md Normal file
View file

@ -0,0 +1,13 @@
The program is build with Qt 6.8 using QtCreator and the Visual Studio 2022 C++ compiler.
Only 64-bits builds are actively maintained.
[Documentation](https://eelke.gitlab.io/pgLab/) is generated automatically when a commit is tagged. It also includes releasenotes.
The release notes use collected from git using [reno](https://docs.openstack.org/reno/latest/index.html).
## Dependencies
- boost
- botan
- googletest
- libpq

View file

@ -4,7 +4,7 @@ error( "Use local.pri.sample to create your own local.pri" )
LIBS += -lUser32 -lws2_32 -llibpq
CONFIG += c++latest
CONFIG += c++20
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings

View file

@ -2,6 +2,7 @@
#define CSVWRITER_H
#include <ostream>
#include <QString>
#include <QTextStream>
class CsvWriter {

View file

@ -1,78 +0,0 @@
#ifndef PASSWORDMANAGER_H
#define PASSWORDMANAGER_H
#include "KeyStrengthener.h"
#include <QSqlDatabase>
#include <botan/secmem.h>
#include <string>
#include <string_view>
#include <tuple>
#include <memory>
#include <botan/pwdhash.h>
#include <map>
namespace Botan {
class Encrypted_PSK_Database;
class PasswordHash;
}
class PasswordManagerException: public std::exception {
public:
using std::exception::exception; //(char const* const _Message);
};
class PasswordManagerLockedException: public PasswordManagerException {
public:
using PasswordManagerException::PasswordManagerException;
};
class PasswordCryptoEngine;
class PasswordManager {
public:
enum Result {
Ok,
Locked,
Error
};
PasswordManager();
~PasswordManager();
/** Check if it has been initialized before.
*
* If returns false then use createDatabase to set it up
* else use openDatabase to get access.
*/
bool initialized(QSqlDatabase &db);
bool createDatabase(QSqlDatabase &db, QString passphrase);
/// Opens the PSK database
bool openDatabase(QSqlDatabase &db, QString passphrase);
void closeDatabase();
bool locked() const;
std::string encrypt(const std::string &id, const std::string &passwd);
std::string decrypt(const std::string &id, const std::string_view &encpwd);
private:
QString m_passwordTableName = "psk_passwd";
QString m_secretAlgoTableName = "psk_masterkey_algo";
QString m_secretHashTableName = "psk_masterkey_hash";
std::unique_ptr<PasswordCryptoEngine> m_cryptoEngine;
bool isPskStoreInitialized(QSqlDatabase& db);
void initializeNewPskStore(QSqlDatabase &db);
/// Get PasswordHash from parameters in database
KeyStrengthener getKeyStrengthener(QSqlDatabase &db);
KeyStrengthener createKeyStrengthener();
std::tuple<Botan::secure_vector<uint8_t>, Botan::secure_vector<uint8_t>>
deriveKey(KeyStrengthener &ks, QString passphrase);
};
#endif // PASSWORDMANAGER_H

View file

@ -255,6 +255,13 @@ bool SqlLexer::parseDoubleQuotedIdentifier(int startpos, int &length, BasicToken
bool SqlLexer::parseDollarQuote(int startpos, int &length, BasicTokenType &tokentype, QString &out)
{
QChar c = nextChar();
if (c == '$') {
tokentype = BasicTokenType::DollarQuote;
length = m_pos - startpos;
out = m_block.mid(startpos, length);
return true;
}
if (c.isDigit()) {
for (;;) {
c = peekChar();

View file

@ -5,7 +5,6 @@
#-------------------------------------------------
QT -= gui
QT += sql
TARGET = core
TEMPLATE = lib
@ -21,16 +20,13 @@ error( "Couldn't find the common.pri file!" )
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += my_boost_assert_handler.cpp \
KeyStrengthener.cpp \
SqlLexer.cpp \
PasswordManager.cpp \
CsvWriter.cpp \
BackupFormatModel.cpp \
ExplainTreeModelItem.cpp \
jsoncpp.cpp
HEADERS += PasswordManager.h \
KeyStrengthener.h \
HEADERS += \
SqlLexer.h \
ScopeGuard.h \
CsvWriter.h \

4
docs/assets/custom.css Normal file
View file

@ -0,0 +1,4 @@
.menuselection {
font-weight: 100
}

View file

@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------
project = 'pgLab User Manual'
copyright = '2021, Eelke Klein'
copyright = '2021-2022, Eelke Klein'
author = 'Eelke Klein'
# The full version, including alpha/beta/rc tags
release = 'https://eelke.gitlab.io/pgLab/'
# release = '1.0'
# -- General configuration ---------------------------------------------------
@ -31,6 +31,7 @@ release = 'https://eelke.gitlab.io/pgLab/'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'reno.sphinxext'
]
# Add any paths that contain templates here, relative to this directory.
@ -47,9 +48,13 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = 'furo'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ['assets']
html_css_files = [
'custom.css'
]

View file

@ -0,0 +1,35 @@
===================
Editing connections
===================
--------------
New connection
--------------
With the :guilabel:`Add connection` button you can create a new connection
configuration.
Entering a password is optional at this stage. You may need it ofcourse if you
want to save the password or test the connection. When you :guilabel:`test` the
connection and the the connection works a list of databases is populated to
select from for the database field.
-----------------
Edit a connection
-----------------
To edit a configuration you need to click the :guilabel:`Configure connection`.
Note the password field will always be empty even if a password was saved. As
long as you do not edit this field the saved password will stay unchanged. As
soon as you change this field or when you clear the :guilabel:`Save password`
checkbox the password will be updated or cleared when you accept your changes
by clicking :guilabel:`OK`.
-----------
Edit a copy
-----------
When you select :guilabel:`Configure copy` you will basically start editing
a new connection with the details of the connection that was selected. Note
the password will not be copied.

View file

@ -0,0 +1,8 @@
==================
Connection Manager
==================
.. toctree::
editingconnection
passwordmanager

View file

@ -0,0 +1,34 @@
================
Password manager
================
The connection manager has a build-in password manager which uses encryption
to savely store passwords. If you want to use it is up to you. Check the Save
password checkbox and the password manager will be used to safely store the
password. The first time this happens you will be asked for a password you wish
to use for the password manager. Make sure to remember this password cause there
is no way to recover it if you loose it.
When you try to open a connection for which the password was saved the program
will prompt you for the password managers password. You can choose how long the
program will keep the password manager unlocked. If you cancel this password
prompt the program will prompt you for the password of the database connection.
------------------------------
Resetting the password manager
------------------------------
If you loose your password and want to start with a new empy password manager
use the menu option :menuselection:`File --> Reset password manager`.
--------
Security
--------
To make it hard to brute force decode the saved passwords the passwords are not
simply encrypted with the master password. Instead a keystrengthening algorithm
is applied to your master password combined with a salt. The exact parameters
choosen for keystrengthening depends on the speed of your computer. This
strengthened key will be used only as a basis for encryption. Each password
saved is associated with a random guid, this 128-bit value is combined with
your strengthened key to create the key to save the password.

View file

@ -0,0 +1,100 @@
###############
Database Window
###############
When opening a database connection the program will open a database window.
Within this window you can only work in a single database and with global
server objects like for instance roles.
The window start with a single query tab.
*************
The Query tab
*************
In the query tab you can enter your queries and execute them with :kbd:`F5` or
:menuselection:`Query --> Execute query`. You can use postgresql's native query
parameters that consist of a `$` followed by a number. The values for these
parameters can be entered in the list to the right of the query editor.
Note that when using parameters postgresql will not accept multiple queries.
A new query tab can be opened with :kbd:`Ctrl+N` or :menuselection:`File --> New query`.
Queries can also be saved with :kbd:`Ctrl+S` or :menuselection:`File --> Save query`
and opened with :kbd:`Ctrl+O` or :menuselection:`File --> Open query`. You can also
drag and drop a bunch of files onto the window and they will be opened in
seperate query tabs.
Explain
=======
You can get a user friendly version of explain output with :kbd:`F7` or :menuselection:`Query --> Explain`.
If you want explain analyze use :kbd:`Shift+F7` or :menuselection:`Query --> Explain analyze`.
Colors are used to highlight the explain nodes that take the most time. Also
keep an eye on the estimation error a high error can indicate stale statistics.
************
Schema views
************
Schema views van be opened from the :menuselection:`View` menu. These views all work
in the same way and get their data from the postgres catalog. The only difference
is the filter used.
User schema
Shows all objects that are not part of a system namespace like pg_catalog or
information schema.
pg_catalog
This is the native postgresql catalog.
information_schema
This is the SQL compatible catalog.
The grids within the schema tabs should all be sortable. Click on the header of
a column to sort by the contents of that column. Click a second time to reverse
the sort order.
Tables
======
Double clicking a table will open a crud tab for that tabel
Note the sizes are retrieved in the background and can take some time to appear
on large databases.
Columns
-------
On the columns tab you can see ofcourse the columns of the table. Note that
this table has as its last columns a column for each index on the table. The
cells contain the position of the table column represented by that row in the
index. Hover over the header to see the name of the index.
When a row is selected SQL for dropping or creating that column and also
to just fix the default or NULL constraint this is useful when inspecting
one database to correct another. When multiple rows are selected SQL for all
those rows is shown.
*********
Crud tabs
*********
If the table had no primary key the data will be readonly. Generated always
columns will ofcourse also be readonly. Use :kbd:`tab` to go from one field to
the next accept your changes with :kbd:`Enter`. To enter a value for a boolean
column use :kbd:`t` for true or :kbd:`f` for false.
An empty value will be seen as a NULL to enter an empty string use two single quotes ''.
When data is saved the program will generate INSERT and UPDATE queries that
return the data that was actually put in the database by the server and update
the data in the grid to match. This includes data for generated columns.
Shortcuts:
* :kbd:`Ctrl+D` delete selected rows.
* :kbd:`F5` reload data
* :kbd:`Ctrl+W` close tab

View file

@ -4,19 +4,22 @@
contain the root `toctree` directive.
pgLab User Manual
====================================================
=================
.. toctree::
:maxdepth: 2
:maxdepth: 3
:caption: Contents:
preface
releasenotes
installation
connectionmanager/index
databasewindow/index
internals
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
Fork this project
==================
* https://eelke.gitlab.io/pgLab/

42
docs/installation.rst Normal file
View file

@ -0,0 +1,42 @@
============
Installation
============
Currently only binaries for Windows 64-bit are provided.
-------
Windows
-------
Downloads can be found `here
<https://eelkeklein.stackstorage.com/s/E9xkMGQDFjHv5XN3>`_.
Installation
============
Unpack the contents of the 7zip archive to a folder of your choosing for
instance `%ProgramFiles%\\pgLab`. You can run the pgLab.exe from there. If
it is complaining about missing files this is probably because the required
Visual C++ Runtime has not yet been installed on your machine you can get it
from `microsoft <https://aka.ms/vs/17/release/vc_redist.x64.exe>`_.
If you want a shortcut you can make one by copying the exe and then choosing
:menuselection:`Paste shortcut` from the context menu in the location where you
want the shortcut.
Upgrade
=======
Note this section assumes you have not saved any of your own data to
the folder in which you unpacked pgLab. If you did move it to another location.
Download the new archive, replace the contents of the folder with the contents
of the new archive. Any shortcuts or pinned taskbar buttons should still work.
Uninstall
=========
Just remove the folder you unpacked pgLab in. If you also want to remove all user
data you should also remove `%AppData%\\pglab` for each user that has used the
program.

12
docs/internals.rst Normal file
View file

@ -0,0 +1,12 @@
=========
Internals
=========
---------
User data
---------
All connection configuration information is stored in
`%AppData%\\pglab\\pglab\\pglabuser.db` this is an sqlite database.

26
docs/preface.rst Normal file
View file

@ -0,0 +1,26 @@
#######
Preface
#######
*************
What is pgLab
*************
pgLab is a PostgreSQL database query tool that also has some views for inspecting
the database schema. It does not provide dialogs for creating new database objects
you can ofcourse execute DDL to create anything you like.
(Note adding dialogs for creating objects is a lot of work. I am not against it
but it has currently no priority for me.)
The strong point of pgLab are:
* separation of different databases into separate windows.
* inspecting of database with subtabs in a single main tab so the list of toplevel tabs stays short.
* very fast processing of query results millions of rows are no problem.
**************
What is it not
**************
If you are unfamiliar with the SQL and need dialogs for doing anything this
is not the tool for you and you are much better of using pgAdmin.

5
docs/releasenotes.rst Normal file
View file

@ -0,0 +1,5 @@
============
Releasenotes
============
.. release-notes::

3
docs/requirements.txt Normal file
View file

@ -0,0 +1,3 @@
reno[sphinx]==3.5.0
Sphinx==5.1.1
furo==2022.6.21

31
pglab/About.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "About.h"
#include "Config.h"
#include <QCoreApplication>
#include <QDesktopServices>
#include <QMessageBox>
#include <QString>
#include <QUrl>
void ShowAboutDialog(QWidget *parent)
{
QString pgLabVersionString = PGLAB_VERSION_STRING;
QString releaseNotesUrl = "https://eelke.gitlab.io/pgLab/releasenotes.html";
QString year = QString::fromLatin1(__DATE__, 11).right(4);
QMessageBox::about(parent, "pgLab", QMessageBox::tr(
"<h1>Version %3</h1>"
"<p><a href=\"%2\">Releasenotes</a></p>"
"<p>Copyrights 2016-%1, Eelke Klein, All Rights Reserved.</p>"
"<p>The program is provided AS IS with NO WARRANTY OF ANY KIND, "
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS "
"FOR A PARTICULAR PURPOSE.</p>"
"<p>This program is dynamically linked with Qt " QT_VERSION_STR " Copyrights "
"the Qt Company Ltd. https://www.qt.io/licensing/.</p>"
"<p>Some icons by <a href=\"https://icons8.com\">Icons8</a> and others by <a href=\"http://www.fatcow.com/free-icons\">fatcow</a> provided under Creative Commons "
"attribution 3.0 license.</p>"
).arg(year).arg(releaseNotesUrl).arg(pgLabVersionString));
}
void OpenManual()
{
QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/"));
}

6
pglab/About.h Normal file
View file

@ -0,0 +1,6 @@
#pragma once
#include <QWidget>
void ShowAboutDialog(QWidget *parent);
void OpenManual();

4
pglab/Config.h Normal file
View file

@ -0,0 +1,4 @@
#pragma once
#define PGLAB_VERSION_STRING "0.3"

View file

@ -102,6 +102,7 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(
edtPassword = new QLineEdit;
SET_OBJECT_NAME(edtPassword);
edtPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit);
connect(edtPassword, &QLineEdit::textEdited, this, &ConnectionConfigurationWidget::passwordEdited);
cbSavePassword = new QCheckBox;
SET_OBJECT_NAME(cbSavePassword);
@ -119,6 +120,7 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(
cmbbxSsl->setModelColumn(0);
auto ssl_model = new SslModeModel(this);
cmbbxSsl->setModel(ssl_model);
cmbbxSsl->setEditable(true);
lblSsl->setBuddy(cmbbxSsl);
lblCert = new QLabel;
@ -205,13 +207,21 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
edtPassword->setText("");
cmbDbname->setCurrentText(cfg.dbname());
cmbbxSsl->setCurrentIndex(static_cast<int>(cfg.sslMode()));
cmbbxSsl->setCurrentText(cfg.getParameter("sslmode"));
edtCert->setText(cfg.sslCert());
edtKey->setText(cfg.sslKey());
edtRootCert->setText(cfg.sslRootCert());
edtCrl->setText(cfg.sslCrl());
edtPassword->setText(cfg.password());
encodedPassword = cfg.encodedPassword();
cbSavePassword->setCheckState(
encodedPassword.isEmpty()
? Qt::CheckState::Unchecked
: Qt::CheckState::Checked
);
passwordChanged = false;
}
ConnectionConfig ConnectionConfigurationWidget::data() const
@ -224,7 +234,7 @@ ConnectionConfig ConnectionConfigurationWidget::data() const
cfg.setUser(edtUser->text());
cfg.setPassword(edtPassword->text());
cfg.setDbname(cmbDbname->currentText());
cfg.setSslMode(static_cast<SslMode>(cmbbxSsl->currentIndex()));
cfg.setParameter("sslmode", cmbbxSsl->currentText());
cfg.setSslCert(edtCert->text());
cfg.setSslKey(edtKey->text());
cfg.setSslRootCert(edtRootCert->text());
@ -238,9 +248,14 @@ QString ConnectionConfigurationWidget::group() const
return cmbbxGroup->currentText();
}
bool ConnectionConfigurationWidget::savePassword() const
bool ConnectionConfigurationWidget::savePasswordEnabled() const
{
return cbSavePassword->isChecked() && !edtPassword->text().isEmpty();
return cbSavePassword->isChecked();
}
bool ConnectionConfigurationWidget::passwordIsChanged() const
{
return passwordChanged;
}
void ConnectionConfigurationWidget::testConnection()
@ -278,3 +293,8 @@ void ConnectionConfigurationWidget::handleTestResult(TestConnectionResult result
cmbDbname->setCurrentText(current);
}
void ConnectionConfigurationWidget::passwordEdited(const QString &)
{
passwordChanged = true;
}

View file

@ -34,7 +34,9 @@ public:
void setData(const ConnectionConfig &cfg);
ConnectionConfig data() const;
QString group() const;
bool savePassword() const;
bool savePasswordEnabled() const;
bool passwordIsChanged() const;
public slots:
void testConnection();
@ -45,6 +47,7 @@ private:
QUuid m_uuid;
QByteArray encodedPassword;
bool passwordChanged;
QLabel *lblGroup;
QComboBox *cmbbxGroup;
@ -77,6 +80,7 @@ private:
QFormLayout *formLayout;
void handleTestResult(TestConnectionResult result);
void passwordEdited(const QString &);
};
#endif // CONNECTIONCONFIGURATIONWIDGET_H

View file

@ -2,11 +2,12 @@
#include "MasterController.h"
#include "ConnectionManagerWindow.h"
#include "ConnectionListModel.h"
#include "PasswordManager.h"
#include "utils/PasswordManager.h"
#include "DatabaseWindow.h"
#include "BackupDialog.h"
#include "PasswordPromptDialog.h"
#include "ConnectionConfigurationWidget.h"
#include <QSqlQuery>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
@ -38,7 +39,6 @@ void ConnectionController::init()
m_connectionTreeModel->load();
m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr);
m_connectionManagerWindow->show();
}
void ConnectionController::showConnectionManager()
@ -53,16 +53,21 @@ void ConnectionController::openSqlWindowForConnection(QModelIndex index)
if (retrieveConnectionPassword(*config)) {
m_connectionTreeModel->save(*config);
// TODO instead of directly openening the mainwindow
// do async connect and only open window when we have
// working connection
auto w = new DatabaseWindow(m_masterController, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(*config);
w->showMaximized();
openSqlWindowForConnection(*config);
}
}
}
void ConnectionController::openSqlWindowForConnection(const ConnectionConfig &config)
{
// TODO instead of directly openening the mainwindow
// do async connect and only open window when we have
// working connection
auto w = new DatabaseWindow(m_masterController, nullptr);
w->setAttribute( Qt::WA_DeleteOnClose );
w->setConfig(config);
w->showMaximized();
}
void ConnectionController::openBackupDlgForConnection(QModelIndex index)
@ -79,10 +84,14 @@ void ConnectionController::openBackupDlgForConnection(QModelIndex index)
}
}
void ConnectionController::createConnection()
void ConnectionController::createConnection(ConnectionConfig *init)
{
ConnectionConfig cc;
cc.setUuid(QUuid::createUuid());
if (init)
{
cc = *init;
cc.setUuid(QUuid());
}
editConfig(cc);
}
@ -99,7 +108,7 @@ void ConnectionController::editCopy(QModelIndex index)
auto config = ConnectionTreeModel::getConfigFromModelIndex(index);
if (config) {
auto cc = *config;
cc.setUuid(QUuid::createUuid());
cc.setUuid(QUuid());
cc.setEncodedPassword({}); // maybe we should decode en reencode?
editConfig(cc);
}
@ -115,8 +124,19 @@ void ConnectionController::saveConnection(ConnectionConfigurationWidget &w)
{
auto cc = w.data();
auto grp = w.group();
if (w.savePassword()) {
encryptPassword(cc);
bool isNew = cc.uuid().isNull();
if (isNew)
{
cc.setUuid(QUuid::createUuid());
}
if (w.savePasswordEnabled())
{
if (isNew || w.passwordIsChanged())
encryptPassword(cc);
}
else
{
cc.setEncodedPassword({});
}
m_connectionTreeModel->save(grp, cc);
}
@ -126,11 +146,14 @@ void ConnectionController::addGroup()
auto result = QInputDialog::getText(nullptr, tr("Add new connection group"),
tr("Group name"));
if (!result.isEmpty()) {
auto res = m_connectionTreeModel->addGroup(result);
if (std::holds_alternative<QSqlError>(res)) {
try
{
m_connectionTreeModel->addGroup(result);
}
catch (const SQLiteException &ex) {
QMessageBox::critical(nullptr, tr("Add group failed"),
tr("Failed to add group.\n") +
std::get<QSqlError>(res).text());
QString(ex.what()));
}
}
}
@ -156,33 +179,36 @@ std::shared_ptr<PasswordManager> ConnectionController::passwordManager()
bool ConnectionController::retrieveConnectionPassword(ConnectionConfig &cc)
{
auto enc_pwd = cc.encodedPassword();
if (!enc_pwd.isEmpty()) {
std::string pw;
auto enc_pwd = cc.encodedPassword();
if (!enc_pwd.isEmpty())
{
std::string pw;
bool result = retrieveFromPasswordManager(getPskId(cc.uuid()),
std::string_view(enc_pwd.data(), enc_pwd.size()) , pw);
if (result) {
cc.setPassword(QString::fromUtf8(pw.data(), pw.size()));
return true;
}
}
// Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord
// ook aan de gebruiker vragen zoals hier gebeurd.
QString str = cc.makeLongDescription();
auto dlg = std::make_unique<PasswordPromptDialog>(PasswordPromptDialog::SaveOption, nullptr);
dlg->setCaption(tr("Connection password prompt"));
dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str));
int exec_result = dlg->exec();
if (result)
{
cc.setPassword(QString::fromUtf8(pw.data(), pw.size()));
return true;
}
}
// Geen else hier want als voorgaande blok niet geretourneerd heeft moeten we wachtwoord
// ook aan de gebruiker vragen zoals hier gebeurd.
QString str = cc.makeLongDescription();
auto dlg = std::make_unique<PasswordPromptDialog>(PasswordPromptDialog::SaveOption, nullptr);
dlg->setCaption(tr("Connection password prompt"));
dlg->setDescription(QString(tr("Please provide password for connection %1")).arg(str));
int exec_result = dlg->exec();
if (exec_result == QDialog::Accepted) {
auto password = dlg->password();
cc.setPassword(password);
if (dlg->saveChecked()) {
if (exec_result == QDialog::Accepted)
{
auto password = dlg->password();
cc.setPassword(password);
if (dlg->saveChecked())
encryptPassword(cc);
}
return true;
}
return false;
return true;
}
return false;
}
bool ConnectionController::retrieveFromPasswordManager(const std::string &password_id, const std::string_view &enc_password, std::string &password)
@ -195,7 +221,7 @@ bool ConnectionController::retrieveFromPasswordManager(const std::string &passwo
password = m_passwordManager->decrypt(password_id, enc_password);
return true;
}
catch (const PasswordManagerException &ex)
catch (const PasswordManagerException &)
{
return false;
}
@ -224,29 +250,43 @@ bool ConnectionController::decodeConnectionPassword(QUuid id, QByteArray encoded
return res;
}
void ConnectionController::resetPasswordManager()
{
SQLiteConnection& user_cfg_db = m_masterController->userConfigDatabase();
SQLiteTransaction tx(user_cfg_db);
m_passwordManager->resetMasterPassword(user_cfg_db);
m_connectionTreeModel->clearAllPasswords();
tx.Commit();
}
bool ConnectionController::UnlockPasswordManagerIfNeeded()
{
auto&& user_cfg_db = m_masterController->userConfigDatabase();
if (m_passwordManager->initialized(user_cfg_db)) {
if (!m_passwordManager->locked())
return true;
auto&& user_cfg_db = m_masterController->userConfigDatabase();
if (m_passwordManager->initialized(user_cfg_db))
{
if (!m_passwordManager->locked())
return true;
while (true) {
// ask user for passphrase
while (true)
{
PassphraseResult pp_result = PassphrasePrompt();
if (!pp_result.success)
break; // leave this retry loop
break;
// user gave OK, if succeeds return true otherwise loop a prompt for password again.
if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase)) {
// user gave OK, if succeeds return true otherwise loop a prompt for password again.
if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase))
{
setRelockTimer(pp_result.rememberForMinutes);
return true;
}
}
}
else {
return true;
}
}
}
else
{
InitializePasswordManager();
}
return true;
}
return false;
}
@ -297,7 +337,7 @@ bool ConnectionController::InitializePasswordManager()
std::string ConnectionController::getPskId(QUuid connectionid)
{
std::string id = "dbpw/";
id += connectionid.toString().toUtf8().data();
id += connectionid.toString(QUuid::WithBraces).toUtf8().data();
return id;
}

View file

@ -12,6 +12,7 @@ class ConnectionTreeModel;
class ConnectionManagerWindow;
class PasswordManager;
class QTimer;
class QSqlDatabase;
class PassphraseResult {
@ -37,11 +38,12 @@ public:
void showConnectionManager();
void openSqlWindowForConnection(QModelIndex index);
void openBackupDlgForConnection(QModelIndex index);
void openSqlWindowForConnection(const ConnectionConfig &cfg);
void openBackupDlgForConnection(QModelIndex index);
/// Starts the form for creating a new conncetion.
/// This function returns immidiatly!
void createConnection();
void createConnection(ConnectionConfig *init = nullptr);
/// Starts the form for editing a conncetion.
/// This function returns immidiatly!
void editConnection(QModelIndex index);
@ -58,6 +60,7 @@ public:
bool UnlockPasswordManagerIfNeeded();
bool decodeConnectionPassword(QUuid id, QByteArray encoded, QString &out_password);
void resetPasswordManager();
private:
MasterController *m_masterController;
ConnectionList *m_connectionList = nullptr;
@ -71,7 +74,6 @@ private:
std::shared_ptr<PasswordManager> m_passwordManager;
bool retrieveFromPasswordManager(const std::string &password_id, const std::string_view &enc_password, std::string &password);
// bool updatePasswordManager(const std::string &password_id, const std::string &password, std::string &enc_password);
/// Expects the plaintext password to be the password that needs encoding.
bool encryptPassword(ConnectionConfig &cc);

View file

@ -1,16 +1,20 @@
#include "ConnectionListModel.h"
#include "ScopeGuard.h"
#include "util.h"
#include <botan/cryptobox.h>
#include <QDir>
#include <QException>
#include <QMimeData>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include <QString>
#include <QStringBuilder>
#include <QStandardPaths>
#include <QStringBuilder>
#include <ranges>
#include <unordered_set>
namespace {
@ -41,59 +45,195 @@ CREATE TABLE IF NOT EXISTS connection (
password TEXT
);)__";
const char * const q_create_table_migrations =
R"__(
CREATE TABLE IF NOT EXISTS _migration (
migration_id TEXT PRIMARY KEY
);)__";
const char * const q_load_migrations_present =
R"__(
SELECT migration_id
FROM _migration;)__";
const char * const q_insert_or_replace_into_connection =
R"__(INSERT OR REPLACE INTO connection
VALUES (:uuid, :name, :conngroup_id, :host, :hostaddr, :port, :user, :dbname,
:sslmode, :sslcert, :sslkey, :sslrootcert, :sslcrl, :password);
// Keeping migration function name and id DRY
#define APPLY_MIGRATION(id) ApplyMigration(#id, &MigrationDirector::id)
class MigrationDirector {
public:
explicit MigrationDirector(SQLiteConnection &db)
: db(db)
{
}
void Execute()
{
InitConnectionTables();
present = LoadMigrations();
APPLY_MIGRATION(M20250215_0933_Parameters);
}
private:
SQLiteConnection &db;
std::unordered_set<QString> present;
void M20250215_0933_Parameters()
{
db.Exec(R"__(
CREATE TABLE connection_parameter (
connection_uuid TEXT,
pname TEXT,
pvalue TEXT NOT NULL,
PRIMARY KEY(connection_uuid, pname)
);)__");
db.Exec(R"__(
INSERT INTO connection_parameter (connection_uuid, pname, pvalue)
SELECT uuid, 'sslmode' AS pname, CASE
WHEN sslmode = '0' THEN 'disable'
WHEN sslmode = '1' THEN 'allow'
WHEN sslmode = '2' THEN 'prefer'
WHEN sslmode = '3' THEN 'require'
WHEN sslmode = '4' THEN 'verify_ca'
WHEN sslmode = '5' THEN 'verify_full'
END AS pvalue
FROM connection
WHERE sslmode is not null and sslmode between 0 and 5)__");
for (QString key : { "host", "hostaddr", "user", "dbname", "sslcert", "sslkey", "sslrootcert", "sslcrl" })
{
db.Exec(
"INSERT INTO connection_parameter (connection_uuid, pname, pvalue)"
" SELECT uuid, '" % key % "', " % key % "\n"
" FROM connection\n"
" WHERE " % key % " IS NOT NULL and " % key % " <> '';");
}
db.Exec(R"__(
INSERT INTO connection_parameter (connection_uuid, pname, pvalue)
SELECT uuid, 'port', port
FROM connection
WHERE port IS NOT NULL;
)__");
for (QString column : { "host", "hostaddr", "user", "dbname", "sslmode", "sslcert", "sslkey", "sslrootcert", "sslcrl", "port" })
{
// sqlite does not seem to support dropping more then one column per alter table
db.Exec("ALTER TABLE connection DROP COLUMN " % column % ";");
}
}
void ApplyMigration(QString migration_id, void (MigrationDirector::*func)())
{
if (!present.contains(migration_id))
{
SQLiteTransaction tx(db);
(this->*func)();
RegisterMigration(migration_id);
tx.Commit();
}
}
std::unordered_set<QString> LoadMigrations()
{
std::unordered_set<QString> result;
auto stmt = db.Prepare(q_load_migrations_present);
while (stmt.Step())
{
result.insert(stmt.ColumnText(0));
}
return result;
}
void RegisterMigration(QString migrationId)
{
auto stmt = db.Prepare("INSERT INTO _migration VALUES (?1);");
stmt.Bind(1, migrationId);
stmt.Step();
}
void InitConnectionTables()
{
// Original schema
db.Exec(q_create_table_conngroup);
db.Exec(q_create_table_connection);
// Start using migrations
db.Exec(q_create_table_migrations);
}
};
void RemoveConnection(SQLiteConnection &db, QUuid uuid)
{
SQLiteTransaction tx(db);
auto stmt = db.Prepare(
"DELETE FROM connection_parameter "
" WHERE connection_uuid=?1");
stmt.Bind(1, uuid.toString());
stmt.Step();
stmt = db.Prepare(
"DELETE FROM connection "
" WHERE uuid=?1");
stmt.Bind(1, uuid.toString());
stmt.Step();
tx.Commit();
}
void SaveConnectionConfig(SQLiteConnection &db, const ConnectionConfig &cc, int conngroup_id)
{
const char * const q_insert_or_replace_into_connection =
R"__(INSERT OR REPLACE INTO connection
VALUES (?1, ?2, ?3, ?4);
)__" ;
std::tuple<bool, QSqlError> InitConnectionTables(QSqlDatabase &db)
{
QSqlQuery q_create_table(db);
q_create_table.prepare(q_create_table_conngroup);
if (!q_create_table.exec()) {
auto err = q_create_table.lastError();
return { false, err };
}
q_create_table.prepare(q_create_table_connection);
if (!q_create_table.exec()) {
auto err = q_create_table.lastError();
return { false, err };
}
return {true, {}};
}
QByteArray b64; // needs to stay in scope until query is executed
SQLiteTransaction tx(db);
std::optional<QSqlError> SaveConnectionConfig(QSqlDatabase &db, const ConnectionConfig &cc, int conngroup_id)
{
QSqlQuery q(db);
q.prepare(q_insert_or_replace_into_connection);
q.bindValue(":uuid", cc.uuid().toString());
q.bindValue(":name", cc.name());
q.bindValue(":conngroup_id", conngroup_id);
q.bindValue(":host", cc.host());
q.bindValue(":hostaddr", cc.hostAddr());
q.bindValue(":port", (int)cc.port());
q.bindValue(":user", cc.user());
q.bindValue(":dbname", cc.dbname());
q.bindValue(":sslmode", static_cast<int>(cc.sslMode()));
q.bindValue(":sslcert", cc.sslCert());
q.bindValue(":sslkey", cc.sslKey());
q.bindValue(":sslrootcert", cc.sslRootCert());
q.bindValue(":sslcrl", cc.sslCrl());
q.bindValue(":password", cc.encodedPassword());
SQLitePreparedStatement stmt = db.Prepare(q_insert_or_replace_into_connection);
stmt.Bind(1, cc.uuid().toString());
stmt.Bind(2, cc.name());
stmt.Bind(3, conngroup_id);
auto& encodedPassword = cc.encodedPassword();
if (!encodedPassword.isEmpty())
{
stmt.Bind(4, encodedPassword.data(), encodedPassword.length());
}
stmt.Step();
if (!q.exec()) {
auto sql_error = q.lastError();
return { sql_error };
}
return {};
stmt = db.Prepare(
"DELETE FROM connection_parameter WHERE connection_uuid=?1");
stmt.Bind(1, cc.uuid().toString());
stmt.Step();
stmt = db.Prepare(
R"__(INSERT INTO connection_parameter (connection_uuid, pname, pvalue)
VALUES(?1, ?2, ?3))__");
const std::unordered_map<QString, QString>& params = cc.getParameters();
for (auto && p : params | std::views::filter(
[] (auto &param)
{
// do not save unencrypted password
return param.first != "password";
}))
{
stmt.Reset();
stmt.Bind(1, cc.uuid().toString());
stmt.Bind(2, p.first);
stmt.Bind(3, p.second);
stmt.Step();
}
tx.Commit();
}
} // end of unnamed namespace
ConnectionTreeModel::ConnectionTreeModel(QObject *parent, QSqlDatabase &db)
ConnectionTreeModel::ConnectionTreeModel(QObject *parent, SQLiteConnection &db)
: QAbstractItemModel(parent)
, m_db(db)
{
@ -101,60 +241,66 @@ ConnectionTreeModel::ConnectionTreeModel(QObject *parent, QSqlDatabase &db)
void ConnectionTreeModel::load()
{
InitConnectionTables(m_db);
//InitConnectionTables(m_db);
MigrationDirector md(m_db);
md.Execute();
QSqlQuery q(m_db);
q.prepare("SELECT conngroup_id, gname FROM conngroup;");
if (!q.exec()) {
// auto err = q_create_table.lastError();
// return { false, err };
throw std::runtime_error("Loading groups failed");
}
while (q.next()) {
int id = q.value(0).toInt();
QString name = q.value(1).toString();
loadGroups();
loadConnections();
}
auto g = std::make_shared<ConnectionGroup>();
g->conngroup_id = id;
g->name = name;
m_groups.push_back(g);
}
void ConnectionTreeModel::loadGroups()
{
auto stmt = m_db.Prepare("SELECT conngroup_id, gname FROM conngroup;");
while (stmt.Step())
{
auto g = std::make_shared<ConnectionGroup>();
g->conngroup_id = stmt.ColumnInteger(0);
g->name = stmt.ColumnText(1);
m_groups.push_back(g);
}
}
q.prepare("SELECT uuid, cname, conngroup_id, host, hostaddr, port, "
" user, dbname, sslmode, sslcert, sslkey, sslrootcert, sslcrl, "
" password "
"FROM connection ORDER BY conngroup_id, cname;");
if (!q.exec()) {
// auto err = q_create_table.lastError();
// return { false, err };
throw std::runtime_error("Loading groups failed");
}
while (q.next()) {
auto cc = std::make_shared<ConnectionConfig>();
cc->setUuid(q.value(0).toUuid());
cc->setName(q.value(1).toString());
cc->setHost(q.value(3).toString());
cc->setHostAddr(q.value(4).toString());
cc->setPort(static_cast<uint16_t>(q.value(5).toInt()));
cc->setUser(q.value(6).toString());
cc->setDbname(q.value(7).toString());
cc->setSslMode(static_cast<SslMode>(q.value(8).toInt()));
cc->setSslCert(q.value(9).toString());
cc->setSslKey(q.value(10).toString());
cc->setSslRootCert(q.value(11).toString());
cc->setSslCrl(q.value(12).toString());
cc->setEncodedPassword(q.value(13).toByteArray());
void ConnectionTreeModel::loadConnections()
{
auto stmt = m_db.Prepare(
"SELECT uuid, cname, conngroup_id, password "
"FROM connection ORDER BY conngroup_id, cname;");
int group_id = q.value(2).toInt();
auto find_res = std::find_if(m_groups.begin(), m_groups.end(),
[group_id] (auto item) { return item->conngroup_id == group_id; });
if (find_res != m_groups.end()) {
(*find_res)->add(cc);
}
else {
throw std::runtime_error("conngroup missing");
}
}
while (stmt.Step()) {
auto cc = std::make_shared<ConnectionConfig>();
cc->setUuid(QUuid::fromString(stmt.ColumnText(0)));
cc->setName(stmt.ColumnText(1));
const char* p = stmt.ColumnCharPtr(3);
cc->setEncodedPassword(p);
loadConnectionParameters(*cc);
int group_id = stmt.ColumnInteger(2);
auto find_res = std::find_if(m_groups.begin(), m_groups.end(),
[group_id] (auto item) { return item->conngroup_id == group_id; });
if (find_res != m_groups.end()) {
(*find_res)->add(cc);
}
else {
throw std::runtime_error("conngroup missing");
}
}
}
void ConnectionTreeModel::loadConnectionParameters(ConnectionConfig &cc)
{
auto stmt = m_db.Prepare(
"SELECT pname, pvalue \n"
"FROM connection_parameter\n"
"WHERE connection_uuid=?1");
stmt.Bind(1, cc.uuid().toString());
while (stmt.Step())
{
cc.setParameter(
stmt.ColumnText(0),
stmt.ColumnText(1)
);
}
}
QVariant ConnectionTreeModel::data(const QModelIndex &index, int role) const
@ -276,15 +422,7 @@ bool ConnectionTreeModel::removeRows(int row, int count, const QModelIndex &pare
auto grp = m_groups[parent.row()];
for (int i = 0; i < count; ++i) {
QUuid uuid = grp->connections().at(row + i)->uuid();
QSqlQuery q(m_db);
q.prepare(
"DELETE FROM connection "
" WHERE uuid=:uuid");
q.bindValue(":uuid", uuid);
if (!q.exec()) {
auto err = q.lastError();
throw std::runtime_error("QqlError");
}
RemoveConnection(m_db, uuid);
}
beginRemoveRows(parent, row, row + count - 1);
SCOPE_EXIT { endRemoveRows(); };
@ -304,7 +442,7 @@ void ConnectionTreeModel::save(const QString &group_name, const ConnectionConfig
grp->update(conn_idx, cc);
// send change event
auto node = grp->connections().at(conn_idx);
dataChanged(
emit dataChanged(
createIndex(conn_idx, 0, node.get()),
createIndex(conn_idx, ColCount-1, node.get()));
saveToDb(*node);
@ -322,14 +460,8 @@ void ConnectionTreeModel::save(const QString &group_name, const ConnectionConfig
// We assume the model is in sync with the DB as the DB should not be shared!
int new_grp_idx = findGroup(group_name);
if (new_grp_idx < 0) {
// Group not found we are g
auto add_grp_res = addGroup(group_name);
if (std::holds_alternative<int>(add_grp_res)) {
new_grp_idx = std::get<int>(add_grp_res);
}
else {
throw std::runtime_error("SqlError1");
}
// Group not found we are g
new_grp_idx = addGroup(group_name);
}
auto new_grp = m_groups[new_grp_idx];
@ -338,20 +470,23 @@ void ConnectionTreeModel::save(const QString &group_name, const ConnectionConfig
beginInsertRows(parent, idx, idx);
SCOPE_EXIT { endInsertRows(); };
auto node = std::make_shared<ConnectionConfig>(cc);
new_grp->add(node);
auto save_res = saveToDb(*node);
if (save_res) {
QString msg = save_res->text()
% "\n" % save_res->driverText()
% "\n" % save_res->databaseText();
throw std::runtime_error(msg.toUtf8().data());
}
new_grp->add(node);
saveToDb(*node);
}
void ConnectionTreeModel::save(const ConnectionConfig &cc)
{
saveToDb(cc);
saveToDb(cc);
}
void ConnectionTreeModel::clearAllPasswords()
{
for (auto group : m_groups)
for (auto cc : group->connections())
{
cc->setEncodedPassword({});
saveToDb(*cc);
}
}
std::tuple<int, int> ConnectionTreeModel::findConfig(const QUuid uuid) const
@ -381,17 +516,13 @@ int ConnectionTreeModel::findGroup(const QString &name) const
return -1;
}
std::variant<int, QSqlError> ConnectionTreeModel::addGroup(const QString &group_name)
int ConnectionTreeModel::addGroup(const QString &group_name)
{
QSqlQuery q(m_db);
q.prepare("INSERT INTO conngroup (gname) VALUES (:name)");
q.bindValue(":name", group_name);
if (!q.exec()) {
auto err = q.lastError();
return { err };
}
auto cg = std::make_shared<ConnectionGroup>();
cg->conngroup_id = q.lastInsertId().toInt();
auto stmt = m_db.Prepare("INSERT INTO conngroup (gname) VALUES (?1)");
stmt.Bind(1, group_name);
auto cg = std::make_shared<ConnectionGroup>();
cg->conngroup_id = m_db.LastInsertRowId();
cg->name = group_name;
int row = m_groups.size();
@ -401,27 +532,21 @@ std::variant<int, QSqlError> ConnectionTreeModel::addGroup(const QString &group_
return row;
}
std::optional<QSqlError> ConnectionTreeModel::removeGroup(int row)
void ConnectionTreeModel::removeGroup(int row)
{
beginRemoveRows({}, row, row);
SCOPE_EXIT { endRemoveRows(); };
auto id = m_groups[row]->conngroup_id;
QSqlQuery q(m_db);
q.prepare("DELETE FROM connection WHERE conngroup_id=:id");
q.bindValue(":id", id);
if (!q.exec()) {
auto err = q.lastError();
return { err };
}
q.prepare("DELETE FROM conngroup WHERE conngroup_id=:id");
q.bindValue(":id", id);
if (!q.exec()) {
auto err = q.lastError();
return { err };
}
auto stmt = m_db.Prepare("DELETE FROM connection WHERE conngroup_id=?1");
stmt.Bind(1, id);
stmt.Step();
stmt = m_db.Prepare("DELETE FROM conngroup WHERE conngroup_id=?1");
stmt.Bind(1, id);
stmt.Step();
m_groups.remove(row);
return {};
}
int ConnectionTreeModel::findGroup(int conngroup_id) const
@ -449,9 +574,9 @@ ConnectionGroup *ConnectionTreeModel::getGroupFromModelIndex(QModelIndex index)
return dynamic_cast<ConnectionGroup*>(node);
}
std::optional<QSqlError> ConnectionTreeModel::saveToDb(const ConnectionConfig &cc)
void ConnectionTreeModel::saveToDb(const ConnectionConfig &cc)
{
return SaveConnectionConfig(m_db, cc, cc.parent()->conngroup_id);
SaveConnectionConfig(m_db, cc, cc.parent()->conngroup_id);
}

View file

@ -11,8 +11,7 @@
#include <variant>
#include <QVector>
#include <QSqlError>
class QSqlDatabase;
#include "sqlite/SQLiteConnection.h"
class ConnectionTreeModel : public QAbstractItemModel {
Q_OBJECT
@ -27,7 +26,7 @@ public:
ColCount
};
ConnectionTreeModel(QObject *parent, QSqlDatabase &db);
ConnectionTreeModel(QObject *parent, SQLiteConnection &db);
void load();
@ -59,9 +58,10 @@ public:
/** Save changed config, group is not allowed to change
*/
void save(const ConnectionConfig &cc);
void clearAllPasswords();
/// Create a new group in the DB and place in the tree
std::variant<int, QSqlError> addGroup(const QString &group_name);
std::optional<QSqlError> removeGroup(int row);
int addGroup(const QString &group_name);
void removeGroup(int row);
int findGroup(int conngroup_id) const;
static ConnectionConfig* getConfigFromModelIndex(QModelIndex index);
@ -70,7 +70,7 @@ public:
private:
using Groups = QVector<std::shared_ptr<ConnectionGroup>>;
QSqlDatabase &m_db;
SQLiteConnection &m_db;
Groups m_groups;
/// Finds the connection with the specified uuid and returns
@ -78,8 +78,11 @@ private:
std::tuple<int, int> findConfig(const QUuid uuid) const;
int findGroup(const QString &name) const;
std::optional<QSqlError> saveToDb(const ConnectionConfig &cc);
void saveToDb(const ConnectionConfig &cc);
void loadGroups();
void loadConnections();
void loadConnectionParameters(ConnectionConfig &cc);
// QAbstractItemModel interface
public:
virtual Qt::DropActions supportedDropActions() const override;

View file

@ -1,14 +1,14 @@
#include "ConnectionManagerWindow.h"
#include "About.h"
#include "ui_ConnectionManagerWindow.h"
#include "MasterController.h"
#include "ConnectionController.h"
#include "ConnectionListModel.h"
#include <QDir>
#include <QMessageBox>
#include <QStandardItemModel>
#include "ConnectionListModel.h"
#include <QDir>
#include <QStandardPaths>
#include <QUrl>
ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidget *parent)
: QMainWindow(parent)
@ -102,3 +102,27 @@ void ConnectionManagerWindow::on_actionConfigureCopy_triggered()
auto ci = ui->treeView->selectionModel()->currentIndex();
m_connectionController->editCopy(ci);
}
void ConnectionManagerWindow::on_actionAbout_triggered()
{
ShowAboutDialog(this);
}
void ConnectionManagerWindow::on_actionManual_triggered()
{
OpenManual();
}
void ConnectionManagerWindow::on_actionReset_password_manager_triggered()
{
auto warning_response = QMessageBox::warning(this, "pgLab", tr(
"Warning you are about to reset the password manager master passwords "
"all stored passwords will be lost! Are you shure you wish to continue?"),
QMessageBox::Yes | QMessageBox::No);
if (warning_response == QMessageBox::Yes)
m_connectionController->resetPasswordManager();
}

View file

@ -39,6 +39,12 @@ private slots:
void on_actionConfigureCopy_triggered();
void on_actionAbout_triggered();
void on_actionManual_triggered();
void on_actionReset_password_manager_triggered();
private:
Ui::ConnectionManagerWindow *ui;
MasterController *m_masterController;

View file

@ -26,16 +26,25 @@
<x>0</x>
<y>0</y>
<width>413</width>
<height>30</height>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>Fi&amp;le</string>
</property>
<addaction name="actionReset_password_manager"/>
<addaction name="actionQuit_application"/>
</widget>
<widget class="QMenu" name="menuHelp">
<property name="title">
<string>Help</string>
</property>
<addaction name="actionManual"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<widget class="QToolBar" name="toolBar">
@ -164,6 +173,26 @@ QToolButton {
<string>Configure copy</string>
</property>
</action>
<action name="actionAbout">
<property name="icon">
<iconset>
<normalon>:/icons/about.png</normalon>
</iconset>
</property>
<property name="text">
<string>About</string>
</property>
</action>
<action name="actionManual">
<property name="text">
<string>Manual</string>
</property>
</action>
<action name="actionReset_password_manager">
<property name="text">
<string>Reset password manager</string>
</property>
</action>
</widget>
<resources>
<include location="resources.qrc"/>

View file

@ -14,9 +14,9 @@ enum class ReferencedType {
};
///
enum DataMeaning {
DataMeaningNormal,
DataMeaningBytes ///< the value represents bytes pretty print in KiB, MiB, GiB, TiB, PiB, EiB
enum class DataMeaning {
Normal,
Bytes ///< the value represents bytes pretty print in KiB, MiB, GiB, TiB, PiB, EiB
};
enum CustomDataRole {

View file

@ -1,4 +1,5 @@
#include "DatabaseWindow.h"
#include "About.h"
#include "ui_DatabaseWindow.h"
#include "util.h"
#include "crud/CrudTab.h"
@ -104,7 +105,7 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config)
try
{
QString title = "pglab - ";
title += m_config.name();
title += m_config.makeLongDescription();
setWindowTitle(title);
auto cfg = m_config;
@ -305,19 +306,7 @@ void DatabaseWindow::dropEvent(QDropEvent *event)
void DatabaseWindow::on_actionAbout_triggered()
{
QMessageBox::about(this, "pgLab 0.1", tr(
"Copyrights 2016-2019, Eelke Klein, All Rights Reserved.\n"
"\n"
"The program is provided AS IS with NO WARRANTY OF ANY KIND, "
"INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS "
"FOR A PARTICULAR PURPOSE.\n"
"\n"
"This program is dynamically linked with Qt 5.12 Copyright (C) 2018 "
"The Qt Company Ltd. https://www.qt.io/licensing/. \n"
"\n"
"Icons by fatcow http://www.fatcow.com/free-icons provided under Creative Commons "
"attribution 3.0 license."
));
ShowAboutDialog(this);
}
@ -453,3 +442,15 @@ void DatabaseWindow::on_actionShow_connection_manager_triggered()
{
m_masterController->connectionController()->showConnectionManager();
}
void DatabaseWindow::on_actionSave_connection_triggered()
{
if (m_config.uuid().isNull())
m_masterController->connectionController()->createConnection(&m_config);
}
void DatabaseWindow::on_actionManual_triggered()
{
OpenManual();
}

View file

@ -105,6 +105,9 @@ private slots:
void on_actionSave_query_as_triggered();
void on_actionSave_copy_of_query_as_triggered();
void on_actionShow_connection_manager_triggered();
void on_actionSave_connection_triggered();
void on_actionManual_triggered();
public:
virtual void setTitleForWidget(QWidget *widget, QString title, QString hint) override;

View file

@ -30,7 +30,7 @@
<x>0</x>
<y>0</y>
<width>1260</width>
<height>29</height>
<height>21</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -44,6 +44,7 @@
<addaction name="actionSave_query_as"/>
<addaction name="actionSave_copy_of_query_as"/>
<addaction name="separator"/>
<addaction name="actionSave_connection"/>
<addaction name="actionExport_data"/>
<addaction name="separator"/>
<addaction name="actionClose"/>
@ -94,6 +95,7 @@
<property name="title">
<string>Help</string>
</property>
<addaction name="actionManual"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
@ -298,11 +300,17 @@
<property name="text">
<string>Refresh</string>
</property>
<property name="shortcut">
<string>F5</string>
</property>
</action>
<action name="actionRefreshCrud">
<property name="text">
<string>Refresh</string>
</property>
<property name="shortcut">
<string>F5</string>
</property>
</action>
<action name="actionSave_query_as">
<property name="text">
@ -319,6 +327,16 @@
<string>Show connection manager</string>
</property>
</action>
<action name="actionManual">
<property name="text">
<string>Manual</string>
</property>
</action>
<action name="actionSave_connection">
<property name="text">
<string>Save connection</string>
</property>
</action>
</widget>
<resources>
<include location="resources.qrc"/>

View file

@ -1,5 +1,8 @@
#include "MasterController.h"
#include "ConnectionController.h"
#include "utils/PostgresqlUrlParser.h"
#include <ConnectionConfig.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
@ -27,18 +30,23 @@ MasterController::~MasterController()
void MasterController::init()
{
m_userConfigDatabase = QSqlDatabase::addDatabase("QSQLITE");
m_userConfigDatabase.setDatabaseName(GetUserConfigDatabaseName());
if (!m_userConfigDatabase.open()) {
qDebug() << "Error: connection with database fail";
}
else {
qDebug() << "Database: connection ok";
}
m_userConfigDatabase.Open(GetUserConfigDatabaseName());
m_connectionController = new ConnectionController(this);
m_connectionController->init();
m_connectionController->init();
QStringList arguments = QCoreApplication::arguments();
for (auto && arg : arguments)
{
ConnectionConfig cfg;
if (TryParsePostgresqlUrl(arg, cfg)) {
m_connectionController->openSqlWindowForConnection(cfg);
return;
}
}
m_connectionController->showConnectionManager();
}
ConnectionController *MasterController::connectionController()
@ -46,7 +54,7 @@ ConnectionController *MasterController::connectionController()
return m_connectionController;
}
QSqlDatabase& MasterController::userConfigDatabase()
SQLiteConnection& MasterController::userConfigDatabase()
{
return m_userConfigDatabase;
}

View file

@ -2,11 +2,11 @@
#define MASTERCONTROLLER_H
#include <QObject>
#include <QSqlDatabase>
#include <atomic>
#include <future>
#include <map>
#include <memory>
#include "sqlite/SQLiteConnection.h"
class ConnectionController;
@ -23,14 +23,14 @@ public:
void init();
ConnectionController* connectionController();
QSqlDatabase& userConfigDatabase();
SQLiteConnection& userConfigDatabase();
signals:
public slots:
private:
QSqlDatabase m_userConfigDatabase;
SQLiteConnection m_userConfigDatabase;
ConnectionController* m_connectionController = nullptr;
};

View file

@ -12,7 +12,7 @@ OpenDatabase::OpenDatabaseSPtr OpenDatabase::createOpenDatabase(const Connection
odb->Init();
return odb;
}
catch (const Pgsql::PgException &ex) {
catch (const std::exception &ex) {
throw OpenDatabaseException(ex.what());
}
}

View file

@ -1,5 +1,4 @@
#include "ResultTableModelUtil.h"
#include "Pgsql_oids.h"
#include <QTableView>
#include <QHeaderView>
@ -28,29 +27,7 @@ Qt::Alignment GetDefaultAlignmentForType(Oid o)
return r;
}
QColor GetDefaultColorForType(Oid o)
{
QColor c;
switch (o) {
case int2_oid:
case int4_oid:
case int8_oid:
c = GetDefaultIntegerColor();
break;
case float4_oid:
case float8_oid:
c = GetDefaultFloatColor();
break;
case numeric_oid:
c = GetDefaultNumericColor();
break;
case oid_oid:
case bool_oid:
default:
c = Qt::black;
}
return c;
}
QString FormatBoolForDisplay(bool v)
{

View file

@ -1,10 +1,36 @@
#pragma once
#include "Pgsql_declare.h"
#include "Pgsql_oids.h"
#include <QAbstractTableModel>
#include <QColor>
#include <util/Colors.h>
Qt::Alignment GetDefaultAlignmentForType(Oid oid);
QColor GetDefaultColorForType(Oid oid);
// inline QColor GetDefaultColorForType(Oid oid)
// {
// using namespace Pgsql;
// const ColorTheme &theme = GetColorTheme();
// QColor c;
// switch (oid) {
// case int2_oid:
// case int4_oid:
// case int8_oid:
// c = theme.integerColor;
// break;
// case float4_oid:
// case float8_oid:
// c = theme.floatColor;
// break;
// case numeric_oid:
// c = theme.numericColor;
// break;
// case oid_oid:
// case bool_oid:
// default:
// c = Qt::black;
// }
// return c;
// }
inline Qt::Alignment GetDefaultAlignment() { return Qt::AlignLeft | Qt::AlignVCenter; }
inline Qt::Alignment GetDefaultBoolAlignment() { return Qt::AlignCenter | Qt::AlignVCenter; }
@ -12,13 +38,14 @@ inline Qt::Alignment GetDefaultNumberAlignment() { return Qt::AlignRight | Qt::A
inline QColor GetDefaultBoolColor(bool v)
{
return v ? Qt::darkGreen : Qt::darkRed;
const ColorTheme& colorTheme = GetColorTheme();
return v ? colorTheme.booleanTrue : colorTheme.booleanFalse;
}
inline QColor GetDefaultIntegerColor() { return Qt::darkBlue; }
inline QColor GetDefaultFloatColor() { return Qt::darkCyan; }
inline QColor GetDefaultNumericColor() { return Qt::darkGreen; }
inline QColor GetDefaultNullColor() { return Qt::gray; }
// inline QColor GetDefaultIntegerColor() { return Qt::darkBlue; }
// inline QColor GetDefaultFloatColor() { return Qt::darkCyan; }
// inline QColor GetDefaultNumericColor() { return Qt::darkGreen; }
// inline QColor GetDefaultNullColor() { return Qt::gray; }
QString FormatBoolForDisplay(bool v);

View file

@ -24,5 +24,5 @@ QVariant BaseTableModel::data(const QModelIndex &index, int role) const
QVariant BaseTableModel::getDataMeaning(const QModelIndex &) const
{
return static_cast<int>(DataMeaningNormal);
return static_cast<int>(DataMeaning::Normal);
}

View file

@ -12,6 +12,7 @@
#include "ScopeGuard.h"
#include "SqlFormattingUtils.h"
#include <QBrush>
#include <QStringBuilder>
namespace {
@ -29,6 +30,13 @@ namespace {
}
ColumnTableModel::ColumnTableModel(QObject *parent)
: BaseTableModel(parent)
, theme(GetColorTheme())
{
}
void ColumnTableModel::setData(std::shared_ptr<const PgDatabaseCatalog> cat, const std::optional<PgClass> &table)
{
if (cat != m_catalog) {
@ -142,18 +150,6 @@ int ColumnTableModel::columnCount(const QModelIndex &/*parent*/) const
Oid ColumnTableModel::getType(int /*column*/) const
{
Oid oid = Pgsql::varchar_oid;
// switch (column) {
// case TypeCol:
// case NameCol:
// case NullCol:
// case DefaultCol:
// case CollationCol:
// oid = VARCHAROID;
// break;
// c = tr("Collation");
// break;
// }
return oid;
}
@ -191,7 +187,7 @@ QVariant ColumnTableModel::getData(const QModelIndex &index) const
v = QString::fromStdU16String(t.notnull ? u"" : u"N");
break;
case DefaultCol:
v = t.defaultValue;
v = getDefaultString(t);
break;
case ForeignKeyCol:
v = getFKey(t);
@ -222,7 +218,36 @@ QString ColumnTableModel::getFKey(const PgAttribute &column) const
result = getForeignKeyConstraintReferencesShort(*m_catalog, elem);
}
}
return result;
return result;
}
QString ColumnTableModel::getDefaultString(const PgAttribute &column) const
{
if (column.identity != '\0') {
QString sql = "GENERATED ";
switch (column.identity) {
case 'a':
sql += "ALWAYS";
break;
case 'd':
sql += "BY DEFAULT";
break;
}
sql += " AS IDENTITY";
// TODO sequence options might be missing
return sql;
}
if (column.generated != '\0')
{
return "GENERATED ALWAYS AS " % column.defaultValue % " STORED";
}
if (column.hasdef)
return column.defaultValue;
return {};
}
void ColumnTableModel::refresh()
@ -267,22 +292,19 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
QVariant v;
const auto &t = m_columns[index.row()];
if (t.typid == InvalidOid)
v = QBrush(Qt::black);
v = QBrush(theme.defaultTextColor);
else {
auto c = m_catalog->types()->getByKey(t.typid);
switch (c->category) {
case TypCategory::Boolean:
v = QBrush(Qt::darkGreen);
break;
case TypCategory::Numeric:
v = QBrush(Qt::darkBlue);
v = QBrush(theme.numericTypeColor);
break;
case TypCategory::DateTime:
case TypCategory::Timespan:
v = QBrush(Qt::darkMagenta);
v = QBrush(theme.dateTimeTypeColor);
break;
case TypCategory::String:
v = QBrush(Qt::darkYellow);
v = QBrush(theme.stringTypeColor);
break;
case TypCategory::Array:
case TypCategory::Composite:
@ -295,7 +317,8 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
case TypCategory::BitString:
case TypCategory::Unknown:
default:
v = QBrush(Qt::black);
v = QBrush(theme.defaultTextColor);
break;
}
}
return v;

View file

@ -1,6 +1,7 @@
#ifndef COLUMNTABLEMODEL_H
#define COLUMNTABLEMODEL_H
#include "util/Colors.h"
#include "catalog/models/BaseTableModel.h"
#include "catalog/PgAttribute.h"
#include "catalog/PgDatabaseCatalog.h"
@ -29,7 +30,8 @@ public:
colCount };
using BaseTableModel::BaseTableModel;
explicit ColumnTableModel(QObject *parent = nullptr);
void setData(std::shared_ptr<const PgDatabaseCatalog> cat, const std::optional<PgClass> &table);
// Header:
@ -55,8 +57,10 @@ protected:
QMetaObject::Connection refreshConnection;
QString getFKey(const PgAttribute &column) const;
QString getDefaultString(const PgAttribute &column) const;
private:
const ColorTheme &theme;
private slots:
/// Retrieves required data from catalog, called everytime it might have changed

View file

@ -55,6 +55,9 @@ QVariant ConstraintModel::headerData(int section, Qt::Orientation orientation, i
case SupportingIndexCol:
c = tr("Supporting index");
break;
case InheritedCol:
c = tr("Inherited");
break;
// case DefinitionCol:
// c = tr("Definition");
// break;
@ -85,11 +88,17 @@ int ConstraintModel::columnCount(const QModelIndex &) const
// return v;
//}
Oid ConstraintModel::getType(int ) const
Oid ConstraintModel::getType(int col) const
{
Oid oid = Pgsql::varchar_oid;
return oid;
Oid oid;
switch (col) {
case InheritedCol:
oid = Pgsql::bool_oid;
break;
default:
oid = Pgsql::varchar_oid;
}
return oid;
}
QString IconForConstraintType(ConstraintType ct)
@ -128,25 +137,27 @@ QVariant ConstraintModel::getData(const QModelIndex &index) const
const auto &t = m_constraints[row];
const int col = index.column();
QString s;
switch (col) {
switch (col) {
case TypeCol:
s = IconForConstraintType(t.type);
v = IconForConstraintType(t.type);
break;
case NameCol:
s = t.objectName();
v = t.objectName();
break;
case NsCol:
s = t.nsName();
v = t.nsName();
break;
case SupportingIndexCol:
s = getIndexDisplayString(*m_catalog, t.indid);
v = getIndexDisplayString(*m_catalog, t.indid);
break;
case InheritedCol:
v = t.isInherited();
break;
// case DefinitionCol:
// s = t.definition;
// break;
}
v = s;
return v;
}

View file

@ -19,6 +19,7 @@ public:
NameCol, ///
NsCol, ///
SupportingIndexCol,
InheritedCol,
// DefinitionCol,
colCount };

View file

@ -5,7 +5,6 @@
#include "catalog/PgDatabaseCatalog.h"
#include "catalog/PgDatabaseContainer.h"
#include "catalog/PgAuthIdContainer.h"
#include "ResultTableModelUtil.h"
#include <QtConcurrent>
#include "Pgsql_Connection.h"
@ -31,7 +30,7 @@ void DatabasesTableModel::setDatabaseList(std::shared_ptr<const PgDatabaseCatalo
for (const auto& d : *dats)
{
databases.emplace_back(d);
oidIndex.emplace(d.oid(), databases.size() - 1);
oidIndex.emplace(d.oid(), static_cast<int>(databases.size() - 1));
}
StartLoadDatabaseSizes(std::move(oidIndex));
}
@ -90,7 +89,7 @@ QVariant DatabasesTableModel::headerData(int section, Qt::Orientation orientatio
int DatabasesTableModel::rowCount(const QModelIndex &) const
{
int result = 0;
result = databases.size();
result = static_cast<int>(databases.size());
return result;
}
@ -183,7 +182,7 @@ QVariant DatabasesTableModel::getDataMeaning(const QModelIndex &index) const
{
if (index.column() == SizeCol)
{
return static_cast<int>(DataMeaningBytes);
return static_cast<int>(DataMeaning::Bytes);
}
return BaseTableModel::getDataMeaning(index);
}

View file

@ -38,9 +38,9 @@ public:
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;
RowItem rowItem(int row) const
RowItem rowItem(const QModelIndex &index) const
{
return databases.at(row).database;
return databases.at(index.row()).database;
}
protected:
virtual QVariant getDataMeaning(const QModelIndex &index) const override;

View file

@ -133,9 +133,9 @@ QVariant IndexModel::data(const QModelIndex &index, int role) const
else if (role == CustomDataMeaningRole) {
switch (index.column()) {
case SizeCol:
return static_cast<int>(DataMeaningBytes);
return static_cast<int>(DataMeaning::Bytes);
default:
return static_cast<int>(DataMeaningNormal);
return static_cast<int>(DataMeaning::Normal);
}
}
return v;

View file

@ -5,15 +5,16 @@
#include "catalog/PgClass.h"
#include "catalog/PgClassContainer.h"
#include "catalog/PgNamespace.h"
#include "catalog/PgNamespaceContainer.h"
#include "Pgsql_declare.h"
#include "catalog/PgInheritsContainer.h"
//#include "Pgsql_declare.h"
#include "ui/catalog/tables/TableTreeBuilder.h"
#include "CustomDataRole.h"
#include <QBrush>
#include <QtConcurrent>
#include "Pgsql_Connection.h"
TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent)
: QAbstractTableModel(parent)
: QAbstractItemModel(parent)
, openDatabase(opendatabase)
{}
@ -33,6 +34,21 @@ void TablesTableModel::setCatalog(std::shared_ptr<const PgDatabaseCatalog> cat)
refresh();
}
bool TableLike(RelKind relkind)
{
switch (relkind) {
case RelKind::Table:
case RelKind::View:
case RelKind::MaterializedView:
case RelKind::ForeignTable:
case RelKind::PartitionedTable:
return true;
default:
return false;
}
}
void TablesTableModel::refresh()
{
beginResetModel();
@ -41,32 +57,19 @@ void TablesTableModel::refresh()
if (!m_catalog)
return;
// Later afscheiden naar filter functie
auto classes = m_catalog->classes();
rootNode.reset();
auto nsfilter = GetNamespaceFilterLambda();
std::map<Oid, PgClass> temp;
for (const auto &e : *classes)
if (TableLike(e.kind) && nsfilter(e))
temp.emplace(e.oid(), e);
TableTreeBuilder treeBuilder(temp, *m_catalog->inherits());
auto [rn, oidIndex] = treeBuilder.Build();
rootNode = rn;
std::map<Oid, int> oidIndex;
m_tables.clear();
for (const auto &e : *classes) {
bool add = false;
if (e.kind == RelKind::Table || e.kind == RelKind::View
|| e.kind == RelKind::MaterializedView || e.kind == RelKind::ForeignTable) {
switch (m_namespaceFilter) {
case NamespaceFilter::User:
add = !e.ns().isSystemCatalog();
break;
case NamespaceFilter::PgCatalog:
add = e.ns().objectName() == "pg_catalog";
break;
case NamespaceFilter::InformationSchema:
add = e.ns().objectName() == "information_schema";
break;
}
}
if (add) {
m_tables.emplace_back(e);
oidIndex.emplace(e.oid(), m_tables.size() - 1);
}
}
StartLoadTableSizes(std::move(oidIndex));
}
@ -93,14 +96,46 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
int TablesTableModel::rowCount(const QModelIndex &) const
int TablesTableModel::rowCount(const QModelIndex &parent) const
{
return static_cast<int>(m_tables.size());
if (parent.isValid())
{
const TableNode *parentItem = static_cast<const TableNode*>(parent.internalPointer());
return static_cast<int>(parentItem->children.size());
}
if (rootNode != nullptr)
return static_cast<int>(rootNode->children.size());
return 0;
}
int TablesTableModel::columnCount(const QModelIndex &) const
{
return colCount;
return colCount;
}
QModelIndex TablesTableModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid())
{
const TableNode *parentItem = static_cast<const TableNode*>(parent.internalPointer());
return createIndex(row, column, parentItem->getChildPtr(row));
}
return createIndex(row, column, rootNode->getChildPtr(row));
}
QModelIndex TablesTableModel::parent(const QModelIndex &index) const
{
if (index.isValid())
{
const TableNode *item = static_cast<const TableNode*>(index.internalPointer());
auto parent = item->parent.lock();
if (parent && parent != rootNode)
{
return createIndex(parent->myIndex, 0, parent.get());
}
}
return {};
}
Oid TablesTableModel::getType(int column) const
@ -129,37 +164,29 @@ Oid TablesTableModel::getType(int column) const
QVariant TablesTableModel::getData(const QModelIndex &index) const
{
const auto &table = m_tables[index.row()];
const auto &t = table._class;
const auto &s = table.sizes;
// const auto &table = rootNode->children[index.row()];
const TableNode* table = nodeFromIndex(index);
const auto &t = table->_class;
const auto &s = table->sizes;
switch (index.column()) {
case NameCol: return t.objectName();
case NamespaceCol: return t.nsName();
case KindCol: return t.typeName();
case OwnerCol: return t.ownerName();
case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace);
case OptionsCol: break;
case AclCol: return t.aclString();
case CommentCol: return t.description;
case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
case NameCol: return t.objectName();
case NamespaceCol: return t.nsName();
case KindCol: return t.typeName();
case OwnerCol: return t.ownerName();
case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace);
case OptionsCol: break;
case AclCol: return t.aclString();
case CommentCol: return t.description;
case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
}
return QVariant();
}
PgClass TablesTableModel::getTable(int row) const
{
return m_tables[row]._class;
}
Oid TablesTableModel::getTableOid(int row) const
{
return m_tables[row]._class.oid();
}
QVariant TablesTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
@ -173,16 +200,16 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const
case TableSizeCol:
case IndexSizeCol:
case ToastSizeCol:
return static_cast<int>(DataMeaningBytes);
return static_cast<int>(DataMeaning::Bytes);
default:
return static_cast<int>(DataMeaningNormal);
return static_cast<int>(DataMeaning::Normal);
}
}
return QVariant();
}
void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex)
void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
{
QPointer p(this);
QtConcurrent::run([this]
@ -192,7 +219,7 @@ void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex)
.then(qApp, [p, oidIndex] (TableSizes sizes)
{
if (p)
p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes));
p.data()->PopulateSizes(oidIndex, sizes);
});
}
@ -241,18 +268,41 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const
return sizes;
}
void TablesTableModel::PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes)
void TablesTableModel::PopulateSizes(
const OidClassIndex &oidIndex,
const std::vector<TableSize> &sizes
)
{
for (auto s : sizes)
{
auto findIter = oidIndex.find(s.oid);
if (findIter != oidIndex.end())
{
m_tables[findIter->second].sizes = s;
findIter->second->sizes = s;
}
}
emit dataChanged(
createIndex(0, TotalSizeCol),
createIndex(static_cast<int>(m_tables.size()), ToastSizeCol)
);
index(0, TotalSizeCol),
index(static_cast<int>(rootNode->children.size()), ToastSizeCol)
);
}
const TableNode* TablesTableModel::nodeFromIndex(const QModelIndex &index)
{
return static_cast<const TableNode*>(index.internalPointer());
}
std::function<bool (const PgClass &)> TablesTableModel::GetNamespaceFilterLambda()
{
switch (m_namespaceFilter) {
case NamespaceFilter::User:
return [] (const PgClass &c) { return !c.ns().isSystemCatalog(); };
case NamespaceFilter::PgCatalog:
return [] (const PgClass &c) { return c.ns().objectName() == "pg_catalog"; };
case NamespaceFilter::InformationSchema:
return [] (const PgClass &c) { return c.ns().objectName() == "information_schema"; };
}
throw std::logic_error("missing case");
}

View file

@ -1,9 +1,12 @@
#ifndef TABLESTABLEMODEL_H
#ifndef TABLESTABLEMODEL_H
#define TABLESTABLEMODEL_H
#include "BaseTableModel.h"
//#include "catalog/models/BaseTableModel.h"
#include "ui/catalog/tables/TableNode.h"
#include "ui/catalog/tables/TableSize.h"
#include "NamespaceFilter.h"
#include "catalog/PgClass.h"
#include <QAbstractItemModel>
#include <memory>
#include <vector>
@ -11,7 +14,7 @@ class OpenDatabase;
class PgClass;
class PgDatabaseCatalog;
class TablesTableModel: public QAbstractTableModel {
class TablesTableModel: public QAbstractItemModel {
public:
using RowItem = PgClass;
enum e_Columns : int {
@ -40,42 +43,31 @@ public:
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
virtual QVariant data(const QModelIndex &index, int role) const override;
PgClass getTable(int row) const;
RowItem rowItem(int row) const
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
QVariant data(const QModelIndex &index, int role) const override;
RowItem rowItem(const QModelIndex &index) const
{
return getTable(row);
return nodeFromIndex(index)->_class;
}
Oid getTableOid(int row) const;
static const TableNode* nodeFromIndex(const QModelIndex &index);
private:
class TableSize {
public:
int oid;
int64_t totalBytes = -1;
int64_t indexBytes = -1;
int64_t toastBytes = -1;
TableSize()
: oid(0)
{}
};
using TableSizes = std::vector<TableSize>;
class Table {
public:
PgClass _class;
TableSize sizes;
Table(const PgClass &cls)
: _class(cls)
{}
};
using t_Tables = std::vector<Table>;
// this is a readonly model so when the vectors have been filled
// they will not change anymore and it is safe to assume the indexes
// of elements do not change
std::shared_ptr<const PgDatabaseCatalog> m_catalog;
NamespaceFilter m_namespaceFilter = NamespaceFilter::User;
t_Tables m_tables;
std::shared_ptr<TableNode> rootNode;
QMetaObject::Connection refreshConnection;
std::shared_ptr<OpenDatabase> openDatabase;
@ -83,10 +75,17 @@ private:
Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const;
void StartLoadTableSizes(std::map<Oid, int> oidIndex);
TableSizes QueryTableSizes() const;
void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes);
using OidClassIndex = std::map<Oid, std::shared_ptr<TableNode>>;
void StartLoadTableSizes(OidClassIndex oidIndex);
TableSizes QueryTableSizes() const;
void PopulateSizes(
const OidClassIndex &oidIndex,
const std::vector<TableSize> &sizes
);
std::function<bool(const PgClass&)> GetNamespaceFilterLambda();
private slots:
void refresh();

View file

@ -34,13 +34,21 @@ void CatalogConstraintPage::setFilter(const std::optional<PgClass> &cls)
void CatalogConstraintPage::tableView_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{
auto rijen = selectedRows();
QString drops;
QString creates;
for (auto rij : rijen) {
const PgConstraint constraint = m_constraintModel->constraint(rij);
drops += constraint.dropSql() % "\n";
creates += constraint.createSql() % "\n";
}
m_definitionView->setPlainText(drops % "\n" % creates);
m_definitionView->setPlainText(
generateSql(
selectedRows()
)
);
}
QString CatalogConstraintPage::generateSql(const std::unordered_set<int> &rijen)
{
QString drops;
QString creates;
for (auto rij : rijen) {
const PgConstraint constraint = m_constraintModel->constraint(rij);
drops += constraint.dropSql() % "\n";
creates += constraint.createSql() % "\n";
}
return drops % "\n" % creates;
}

View file

@ -22,6 +22,7 @@ private:
ConstraintModel *m_constraintModel = nullptr;
QString generateSql(const std::unordered_set<int> &rows);
private slots:
void tableView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
};

View file

@ -17,7 +17,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr<OpenDatabase> open_database,
: QWidget(parent)
{
m_tabWidget = new QTabWidget(this);
// m_namespacePage = new CatalogNamespacePage(this);
m_namespacePage = new CatalogNamespacePage(this);
m_tablesPage = new CatalogTablesPage(open_database, this);
m_functionsPage = new CatalogFunctionsPage(this);
m_sequencesPage = new CatalogSequencesPage(this);
@ -26,7 +26,7 @@ CatalogInspector::CatalogInspector(std::shared_ptr<OpenDatabase> open_database,
auto layout = new QVBoxLayout(this);
setLayout(layout);
layout->addWidget(m_tabWidget);
// m_tabWidget->addTab(m_namespacePage, "");
m_tabWidget->addTab(m_namespacePage, "");
m_tabWidget->addTab(m_tablesPage, "");
m_tabWidget->addTab(m_functionsPage, "");
m_tabWidget->addTab(m_sequencesPage, "");
@ -40,8 +40,8 @@ void CatalogInspector::retranslateUi(bool all)
{
m_tablesPage->retranslateUi(all);
// m_tabWidget->setTabText(m_tabWidget->indexOf(m_namespacePage),
// QApplication::translate("CatalogInspector", "Schemas", nullptr));
m_tabWidget->setTabText(m_tabWidget->indexOf(m_namespacePage),
QApplication::translate("CatalogInspector", "Schemas", nullptr));
m_tabWidget->setTabText(m_tabWidget->indexOf(m_tablesPage),
QApplication::translate("CatalogInspector", "Tables", nullptr));
m_tabWidget->setTabText(m_tabWidget->indexOf(m_functionsPage),
@ -59,7 +59,7 @@ CatalogInspector::~CatalogInspector()
void CatalogInspector::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{
m_catalog = cat;
// m_namespacePage->setCatalog(cat);
m_namespacePage->setCatalog(cat);
m_tablesPage->setCatalog(cat);
m_functionsPage->setCatalog(cat);
m_sequencesPage->setCatalog(cat);

View file

@ -26,7 +26,7 @@ public:
CatalogTablesPage *tablesPage();
private:
QTabWidget *m_tabWidget = nullptr;
// CatalogNamespacePage *m_namespacePage = nullptr;
CatalogNamespacePage *m_namespacePage = nullptr;
CatalogTablesPage *m_tablesPage = nullptr;
CatalogFunctionsPage *m_functionsPage = nullptr;
CatalogSequencesPage *m_sequencesPage = nullptr;

View file

@ -8,7 +8,7 @@
#include "catalog/widgets/TriggerPage.h"
#include "catalog/models/ColumnTableModel.h"
#include "catalog/models/ConstraintModel.h"
#include "catalog/models/TablesTableModel.h"
#include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableView.h"
#include "ResultTableModelUtil.h"
#include "widgets/SqlCodePreview.h"
@ -27,7 +27,7 @@ CatalogTablesPage::CatalogTablesPage(std::shared_ptr<OpenDatabase> opendatabase,
: QSplitter(Qt::Horizontal, parent)
, m_tablesTableView(this, new TablesTableModel(opendatabase, this))
{
auto tv = m_tablesTableView.tableView();
auto tv = m_tablesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this);
@ -106,7 +106,7 @@ void CatalogTablesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
void CatalogTablesPage::setNamespaceFilter(NamespaceFilter filter)
{
m_tablesTableView.dataModel()->setNamespaceFilter(filter);
m_tablesTableView.tableView()->resizeColumnsToContents();
//m_tablesTableView.tableView()->resizeColumnsToContents();
}
void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &current, const QModelIndex &previous)
@ -114,8 +114,8 @@ void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &curr
if (current.row() == previous.row())
return;
auto table = m_tablesTableView.rowItemForProxyIndex(current);
selectedTableChanged(table);
auto tableNode = TablesTableModel::nodeFromIndex(m_tablesTableView.sortFilter()->mapToSource(current)); // m_tablesTableView.rowItemForProxyIndex(current);
selectedTableChanged(tableNode->_class);
}
@ -127,11 +127,15 @@ void CatalogTablesPage::tableListTable_layoutChanged(const QList<QPersistentMode
void CatalogTablesPage::on_tableListTable_doubleClicked(const QModelIndex &index)
{
auto row = m_tablesTableView.sortFilter()->mapToSource(index).row();
PgClass table = m_tablesTableView.dataModel()->getTable(row);
if (table.oid() != InvalidOid) {
tableSelected(table.oid());
}
auto sourceIndex = m_tablesTableView.sortFilter()->mapToSource(index);
auto tableNode = TablesTableModel::nodeFromIndex(sourceIndex);
if (tableNode)
{
Oid oid = tableNode->_class.oid();
if (oid != InvalidOid) {
tableSelected(oid);
}
}
}
void CatalogTablesPage::selectedTableChanged(const std::optional<PgClass> &table)

View file

@ -2,13 +2,13 @@
#define CATALOGTABLESPAGE_H
#include "NamespaceFilter.h"
#include "catalog/models/TablesTableModel.h"
#include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableViewHelper.h"
#include <QSplitter>
#include <memory>
#include <optional>
#include <QAbstractItemModel>
#include "Pgsql_oids.h"
class CatalogConstraintPage;
class CatalogIndexPage;
@ -39,7 +39,7 @@ public:
signals:
void tableSelected(Oid tableoid);
private:
PgLabTableViewHelper<TablesTableModel> m_tablesTableView;
PgLabTableViewHelper<TablesTableModel, QTreeView> m_tablesTableView;
// Details
QTabWidget *m_detailsTabs = nullptr;

View file

@ -14,6 +14,7 @@
CodeEditor::CodeEditor(QWidget *parent)
: QPlainTextEdit(parent)
, gutterArea(new EditorGutter(this))
, colorTheme(GetColorTheme())
{
connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateGutterAreaWidth(int)));
connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateGutterArea(QRect,int)));
@ -69,8 +70,7 @@ void CodeEditor::resizeEvent(QResizeEvent *e)
void CodeEditor::highlightCurrentLine()
{
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setBackground(colorTheme.currentLine);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
@ -87,8 +87,7 @@ void CodeEditor::onTextChanged()
void CodeEditor::addErrorMarker(int position, int length)
{
QTextEdit::ExtraSelection selection;
QColor lineColor = QColor(Qt::red).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setBackground(colorTheme.errorLine);
selection.format.setFontItalic(true);
selection.cursor = textCursor();
selection.cursor.setPosition(position);

View file

@ -3,6 +3,7 @@
#include <QPlainTextEdit>
#include <set>
#include "util/Colors.h"
/** This class adds some capabilities to QPlainTextEdit that are useful
* in code editor scenarios.
@ -39,6 +40,7 @@ private:
QTextEdit::ExtraSelection currentLine;
QList<QTextEdit::ExtraSelection> errorMarkers;
const ColorTheme &colorTheme;
std::set<int> errorLines;

View file

@ -7,11 +7,14 @@ GutterPainter::GutterPainter(CodeEditor *editor, QPaintEvent *event)
, painter(editor->gutterArea)
, event(event)
, fontMetrics(editor->fontMetrics())
{}
, colorTheme(GetColorTheme())
{
}
void GutterPainter::Paint()
{
painter.fillRect(event->rect(), Qt::lightGray);
painter.fillRect(event->rect(), colorTheme.gutterBackground);
LoopState loopState(editor);
// We will now loop through all visible lines and paint the line numbers in the
@ -37,7 +40,7 @@ void GutterPainter::Paint()
void GutterPainter::drawLineNumber(const LoopState &loopState)
{
QString number = QString::number(loopState.blockNumber() + 1);
painter.setPen(Qt::black);
painter.setPen(colorTheme.lineNumber);
painter.drawText(0, loopState.top, editor->gutterArea->width(), fontMetrics.height(),
Qt::AlignRight, number);
}

View file

@ -1,5 +1,6 @@
#include <QPainter>
#include <QTextBlock>
#include "util/Colors.h"
#pragma once
@ -13,10 +14,13 @@ public:
void Paint();
private:
CodeEditor *editor;
QPainter painter;
QPaintEvent *event;
QFontMetrics fontMetrics;
const ColorTheme &colorTheme;
struct LoopState {
CodeEditor *editor;

View file

@ -169,7 +169,7 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const
if (index.column() < PreColumnCount)
{
if (role == Qt::DisplayRole || role == CustomSortRole)
return index.row();
return index.row() + 1;
else if (role == CustomDataTypeRole)
return Pgsql::int4_oid;
@ -231,9 +231,10 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
beginResetModel();
m_pendingRowList.clear();
m_roData = data;
initializeColumnList();
lastRowKey = data->rows() - 1;
initRowMapping();
appendNewRow();
appendNewRowInternal();
endResetModel();
}
@ -246,9 +247,9 @@ void CrudModel::initRowMapping()
m_rowMapping.emplace_back(i);
}
void CrudModel::connectionStateChanged(ASyncDBConnection::StateData state)
void CrudModel::connectionStateChanged()
{
switch (state.State) {
switch (m_dbConn.state()) {
case ASyncDBConnection::State::NotConnected:
break;
case ASyncDBConnection::State::Connecting:
@ -276,7 +277,7 @@ Qt::ItemFlags CrudModel::flags(const QModelIndex &index) const
if (index.column() < PreColumnCount)
return flags;
if (m_primaryKey)
if (m_primaryKey && !columnIsReadOnly(index.column()))
flags |= Qt::ItemIsEditable;
return flags;
@ -419,21 +420,16 @@ std::tuple<QString, Pgsql::Params> CrudModel::createInsertQuery(const PendingRow
QTextStream q(&buffer);
q << "INSERT INTO " << table_name << "(";
auto columns = m_database->catalog()->attributes()->getColumnsForRelation(m_table->oid());
bool first = true;
for (const auto& e : data)
{
int num = e.first + 1;
auto find_res = std::find_if(columns.begin(), columns.end(),
[num] (const auto &elem) -> bool { return num == elem.num; });
if (find_res != columns.end())
{
if (first)
first = false;
else
q << ",";
q << find_res->name;
}
int num = e.first;
auto&& column = columnList[num];
if (first)
first = false;
else
q << ",";
q << quoteIdent(column.name);
}
q << ") VALUES ($1";
for (size_t p = 2; p <= data.size(); ++p)
@ -515,6 +511,30 @@ bool CrudModel::IsLastRow(RowMappingVector::iterator mapping_iter) const
return mapping_iter == --m_rowMapping.end();
}
bool CrudModel::columnIsReadOnly(int column_index) const
{
if (m_roData == nullptr)
return true;
auto&& column = columnList[column_index - PreColumnCount];
return column.getIdentity() == PgAttribute::Identity::Always
|| column.getGenerated() != PgAttribute::Generated::None;
}
void CrudModel::initializeColumnList()
{
columnList.clear();
columnList.reserve(m_roData->cols());
auto columns = m_database->catalog()->attributes()->getColumnsForRelation(m_table->oid());
for (int col = 0; col < m_roData->cols(); ++col)
{
int attnum = m_roData->ftableCol(col);
auto find_result = std::find_if(columns.begin(), columns.end(), [attnum](const PgAttribute &att) { return att.num == attnum; });
assert(find_result != columns.end());
columnList.push_back(*find_result);
}
}
bool CrudModel::submit()
{
return savePendingChanges();
@ -527,12 +547,17 @@ void CrudModel::revert()
void CrudModel::appendNewRow()
{
int row = m_rowMapping.size();
int row = static_cast<int>(m_rowMapping.size());
beginInsertRows(QModelIndex(), row, row);
m_rowMapping.emplace_back(allocNewRowKey(), std::vector<Value>(m_roData->cols()));
appendNewRowInternal();
endInsertRows();
}
void CrudModel::appendNewRowInternal()
{
m_rowMapping.emplace_back(allocNewRowKey(), std::vector<Value>(m_roData->cols()));
}
std::tuple<bool, QString> CrudModel::removeRows(const std::set<IntegerRange<int>> &row_ranges)
{
if (row_ranges.empty())

View file

@ -15,6 +15,7 @@
#include <vector>
#include <optional>
class PgAttribute;
class PgConstraint;
class OpenDatabase;
@ -210,6 +211,7 @@ private:
bool callLoadData = false;
std::shared_ptr<Pgsql::Result> m_roData;
std::vector<PgAttribute> columnList; // list of columnMeta 1 to 1 with columns in m_roData.
PendingRowList m_pendingRowList;
@ -244,6 +246,7 @@ private:
std::tuple<bool, std::vector<Value>> saveRow(const PendingRow &pending_row);
void appendNewRow();
void appendNewRowInternal();
int lastRowKey = -1;
int allocNewRowKey() { return ++lastRowKey; }
@ -256,10 +259,12 @@ private:
void RemoveRangeOfRowsFromModel(IntegerRange<int> row_range);
bool IsLastRow(RowMappingVector::iterator mapping_iter) const;
bool columnIsReadOnly(int column_index) const;
void initializeColumnList();
private slots:
void loadIntoModel(std::shared_ptr<Pgsql::Result> data);
void connectionStateChanged(ASyncDBConnection::StateData state);
void connectionStateChanged();
};
#endif // CRUDMODEL_H

View file

@ -1,8 +1,10 @@
#include "MasterController.h"
#include "util/Colors.h"
#include <QApplication>
#ifdef _WIN32
# include <winsock2.h>
#endif
#include <QPalette>
#include <memory>
int main(int argc, char *argv[])
@ -28,6 +30,8 @@ int main(int argc, char *argv[])
QCoreApplication::setOrganizationDomain("eelkeklein.nl");
QCoreApplication::setApplicationName("pglab");
InitApplicationPalette();
int result = -1;
{
// make sure the io_service is stopped before we wait on the future

View file

@ -21,6 +21,7 @@ DEFINES += _WIN32_WINNT=0x0501
win32:RC_ICONS += pglab.ico
SOURCES += main.cpp\
About.cpp \
catalog/delegates/IconColumnDelegate.cpp \
catalog/models/BaseTableModel.cpp \
catalog/models/ColumnTableModel.cpp \
@ -31,8 +32,8 @@ SOURCES += main.cpp\
catalog/models/ProcTableModel.cpp \
catalog/models/RolesTableModel.cpp \
catalog/models/SequenceModel.cpp \
catalog/models/TablesTableModel.cpp \
catalog/models/TriggerTableModel.cpp \
catalog/tables/TablesTableModel.cpp \
catalog/widgets/CatalogConstraintPage.cpp \
catalog/widgets/CatalogFunctionsPage.cpp \
catalog/widgets/CatalogIndexPage.cpp \
@ -57,6 +58,7 @@ SOURCES += main.cpp\
serverinspector/DatabasesPage.cpp \
serverinspector/RolesPage.cpp \
serverinspector/ServerInspector.cpp \
util/Colors.cpp \
util/PgLabItemDelegate.cpp \
util/PgLabTableView.cpp \
util/SqlSyntaxHighlighter.cpp \
@ -91,6 +93,8 @@ SOURCES += main.cpp\
widgets/SingleRecordWidget.cpp
HEADERS += \
About.h \
Config.h \
catalog/delegates/IconColumnDelegate.h \
catalog/models/BaseTableModel.h \
catalog/models/ColumnTableModel.h \
@ -101,8 +105,8 @@ HEADERS += \
catalog/models/ProcTableModel.h \
catalog/models/RolesTableModel.h \
catalog/models/SequenceModel.h \
catalog/models/TablesTableModel.h \
catalog/models/TriggerTableModel.h \
catalog/tables/TablesTableModel.h \
catalog/widgets/CatalogConstraintPage.h \
catalog/widgets/CatalogFunctionsPage.h \
catalog/widgets/CatalogIndexPage.h \
@ -127,6 +131,7 @@ HEADERS += \
serverinspector/DatabasesPage.h \
serverinspector/RolesPage.h \
serverinspector/ServerInspector.h \
util/Colors.h \
util/PgLabItemDelegate.h \
util/PgLabTableView.h \
util/PgLabTableViewHelper.h \

View file

@ -16,6 +16,7 @@ const int c_NumberOfColumns = 8;
QueryExplainModel::QueryExplainModel(QObject *parent, ExplainRoot::SPtr exp)
: QAbstractItemModel(parent)
, explain(std::move(exp))
, theme(GetColorTheme())
{}
QVariant QueryExplainModel::data(const QModelIndex &index, int role) const
@ -67,35 +68,35 @@ if (role == Qt::DisplayRole) {
if (tt > 0.000000001f) {
float f = t / tt;
if (f > 0.9f) {
result = QColor(255, 192, 192);
result = theme.explainTime[0];
}
else if (f > 0.63f) {
result = QColor(255, 224, 192);
result = theme.explainTime[1];
}
else if (f > 0.36f) {
result = QColor(255, 255, 192);
result = theme.explainTime[2];
}
else if (f > 0.09f) {
result = QColor(255, 255, 224);
result = theme.explainTime[3];
}
else {
result = QColor(Qt::white);
result = {};
}
}
}
if (col == c_ColumnEstErr) {
float e = std::fabs(item->estimateError());
if (e > 1000.0f) {
result = QColor(255, 192, 192);
result = theme.explainEstError[0];
}
else if (e > 100.0f) {
result = QColor(255, 224, 192);
result = theme.explainEstError[1];
}
else if (e > 10.0f) {
result = QColor(255, 255, 192);
result = theme.explainEstError[2];
}
else {
result = QColor(Qt::white);
result = {}; //QColor(Qt::white);
}
}
}

View file

@ -2,6 +2,7 @@
#include <QAbstractItemModel>
#include <string>
#include <util/Colors.h>
#include "ExplainTreeModelItem.h"
/** \brief Model class for displaying the explain of a query in a tree like format.
@ -30,4 +31,5 @@ public:
private:
ExplainRoot::SPtr explain;
const ColorTheme &theme;
};

View file

@ -217,7 +217,7 @@
<customwidget>
<class>CodeEditor</class>
<extends>QPlainTextEdit</extends>
<header>CodeEditor.h</header>
<header>codeeditor/CodeEditor.h</header>
</customwidget>
</customwidgets>
<resources/>

View file

@ -322,13 +322,14 @@ void QueryTool::queryTextChanged()
setQueryTextChanged(true);
}
void QueryTool::connectionStateChanged(ASyncDBConnection::StateData state)
void QueryTool::connectionStateChanged()
{
QString iconname;
switch (state.State) {
switch (m_dbConnection.state()) {
case ASyncDBConnection::State::NotConnected:
QMessageBox::warning(this, "pglab", tr("Warning connection and any of its session state has been lost"));
startConnect();
//startConnect();
iconname = "red.png";
break;
case ASyncDBConnection::State::Connecting:
@ -586,7 +587,9 @@ void QueryTool::generateCode()
{
QString command = getCommand();
if (resultList.empty()) {
QMessageBox::question(this, "pglab", tr("Please execute the query first"), QMessageBox::Ok);
QMessageBox::question(this, "pglab", tr("Please execute the query first"),
QMessageBox::StandardButtons(QMessageBox::Ok),
QMessageBox::Ok);
}
if (resultList.size() == 1) {
std::shared_ptr<const Pgsql::Result> dbres = resultList[0]->GetPgsqlResult();

View file

@ -113,7 +113,7 @@ private slots:
void query_ready(std::shared_ptr<Pgsql::Result>, qint64 elapsedms);
void queryTextChanged();
void connectionStateChanged(ASyncDBConnection::StateData state);
void connectionStateChanged();
void receiveNotice(Pgsql::ErrorDetails notice);
void startConnect();

View file

@ -12,7 +12,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget
: QSplitter(Qt::Horizontal, parent)
, m_databasesTableView(this, new DatabasesTableModel(opendatabase, this))
{
auto tv = m_databasesTableView.tableView();
auto tv = m_databasesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
tv->setContextMenuPolicy(Qt::ActionsContextMenu);
@ -23,7 +23,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr<OpenDatabase> opendatabase, QWidget
m_tableSql = new SqlCodePreview(this);
addWidget(m_tableSql);
connect(m_databasesTableView.tableView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
connect(m_databasesTableView.itemView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &DatabasesPage::databaseSelectionChanged);
connect(createDbAction, &QAction::triggered,
[this](auto checked)
@ -37,7 +37,7 @@ void DatabasesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{
m_catalog = cat;
m_databasesTableView.dataModel()->setDatabaseList(cat);
m_databasesTableView.tableView()->resizeColumnsToContents();
m_databasesTableView.itemView()->resizeColumnsToContents();
}
void DatabasesPage::updateDatabaseDetails(const PgDatabase &db)

View file

@ -6,7 +6,7 @@ RolesPage::RolesPage(QWidget * parent)
: QSplitter(Qt::Horizontal, parent)
, m_rolesTableView(this)
{
auto tv = m_rolesTableView.tableView();
auto tv = m_rolesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this);
@ -19,5 +19,5 @@ void RolesPage::setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{
m_catalog = cat;
m_rolesTableView.dataModel()->setRoleList(cat->authIds());
m_rolesTableView.tableView()->resizeColumnsToContents();
m_rolesTableView.itemView()->resizeColumnsToContents();
}

165
pglab/util/Colors.cpp Normal file
View file

@ -0,0 +1,165 @@
#include "Colors.h"
#include <QGuiApplication>
#include <QPalette>
#include <QColor>
bool isDarkModeInternal()
{
QPalette palette = QGuiApplication::palette();
QColor backgroundColor = palette.color(QPalette::Window);
return backgroundColor.lightness() < 128; // Check if the background color is dark
}
bool isDarkMode()
{
static bool darkMode = isDarkModeInternal();
return darkMode;
}
ColorTheme GetLightMode()
{
ColorTheme t;
t.defaultTextColor = Qt::black;
t.gutterBackground = Qt::lightGray;
t.lineNumber = Qt::black;
t.currentLine = QColor(Qt::yellow).lighter(160);
t.errorLine = QColor(Qt::red).lighter(160);
t.keyword = QColor(32, 32, 192);
t.comment = QColor(128, 128, 128);
t.quotedString = QColor(192, 32, 192);
t.quotedIdentifier = QColor(192, 128, 32);
t.type = QColor(32, 192, 32);
t.parameter = QColor(192, 32, 32);
t.gigaBytes = QColorConstants::Svg::darkorange;
t.megaBytes = QColorConstants::Svg::darkgoldenrod;
t.kiloBytes = QColorConstants::Svg::darkgreen;
t.bytes = QColorConstants::Svg::darkblue;
t.booleanTrue = Qt::darkGreen;
t.booleanFalse = Qt::darkRed;
t.integerColor = Qt::darkBlue;
t.floatColor = Qt::darkCyan;
t.numericColor = Qt::darkGreen;
t.nullColor = Qt::gray;
t.numericTypeColor = Qt::darkBlue;
t.dateTimeTypeColor = Qt::darkMagenta;
t.stringTypeColor = Qt::darkYellow;
t.explainTime[0] = QColor(255, 192, 192);
t.explainTime[1] = QColor(255, 224, 192);
t.explainTime[2] = QColor(255, 255, 192);
t.explainTime[3] = QColor(255, 255, 224);
t.explainEstError[0] = QColor(255, 192, 192);
t.explainEstError[1] = QColor(255, 224, 192);
t.explainEstError[2] = QColor(255, 255, 192);
return t;
}
ColorTheme GetDarkMode()
{
ColorTheme t;
t.defaultTextColor = Qt::lightGray;
t.gutterBackground = QColor(Qt::darkGray).darker(400);
t.lineNumber = Qt::lightGray;
t.currentLine = QColor(Qt::darkGray).darker(400);;
t.errorLine = QColor(Qt::red).darker(180);
t.keyword = QColor(160, 160, 255);
t.comment = QColor(160, 160, 160);
t.quotedString = QColor(255, 160, 255);
t.quotedIdentifier = QColor(255, 192, 128);
t.type = QColor(160, 255, 160);
t.parameter = QColor(255, 160, 160);
t.gigaBytes = QColorConstants::Svg::lightcoral;
t.megaBytes = QColorConstants::Svg::lightgoldenrodyellow;
t.kiloBytes = QColorConstants::Svg::lightgreen;
t.bytes = QColorConstants::Svg::lightblue;
t.booleanTrue = QColorConstants::Svg::lightgreen;
t.booleanFalse = QColorConstants::Svg::lightcoral;
t.integerColor = QColorConstants::Svg::deepskyblue;
t.floatColor = QColorConstants::Svg::cyan;
t.numericColor = QColorConstants::Svg::lime;
t.nullColor = Qt::darkGray;
t.numericTypeColor = QColorConstants::Svg::lightblue;
t.dateTimeTypeColor = QColor(Qt::magenta);
t.stringTypeColor = QColor(Qt::yellow);
t.explainTime[0] = QColor(120, 20, 20);
t.explainTime[1] = QColor(110, 40, 20);
t.explainTime[2] = QColor(90, 50, 20);
t.explainTime[3] = QColor(70, 70, 20);
t.explainEstError[0] = QColor(120, 20, 20);
t.explainEstError[1] = QColor(90, 50, 20);
t.explainEstError[2] = QColor(70, 70, 20);
return t;
}
const ColorTheme& GetColorTheme()
{
static ColorTheme lightMode = GetLightMode();
static ColorTheme darkMode = GetDarkMode();
return isDarkMode() ? darkMode : lightMode;
}
void InitApplicationPalette()
{
if (isDarkMode())
{
QPalette pal = QGuiApplication::palette();
pal.setColor(QPalette::Active, QPalette::Base, QColor(20, 20, 20));
pal.setColor(QPalette::Active, QPalette::AlternateBase, Qt::black);
pal.setColor(QPalette::Inactive, QPalette::Base, QColor(20, 20, 20));
pal.setColor(QPalette::Inactive, QPalette::AlternateBase, Qt::black);
QGuiApplication::setPalette(pal);
}
}
QColor ColorTheme::GetColorForType(Oid oid) const
{
using namespace Pgsql;
QColor c;
switch (oid) {
case int2_oid:
case int4_oid:
case int8_oid:
c = integerColor;
break;
case float4_oid:
case float8_oid:
c = floatColor;
break;
case numeric_oid:
c = numericColor;
break;
case varchar_oid:
case text_oid:
case oid_oid:
case bool_oid:
default:
c = defaultTextColor;
}
return c;
}

50
pglab/util/Colors.h Normal file
View file

@ -0,0 +1,50 @@
#include <QColor>
#include "Pgsql_oids.h"
#pragma once
bool isDarkMode();
struct ColorTheme
{
QColor defaultTextColor;
QColor gutterBackground;
QColor lineNumber;
QColor currentLine;
QColor errorLine;
QColor keyword;
QColor comment;
QColor quotedString;
QColor quotedIdentifier;
QColor type;
QColor parameter;
QColor gigaBytes;
QColor megaBytes;
QColor kiloBytes;
QColor bytes;
QColor booleanTrue;
QColor booleanFalse;
QColor integerColor;
QColor floatColor;
QColor numericColor;
QColor nullColor;
QColor numericTypeColor;
QColor dateTimeTypeColor;
QColor stringTypeColor;
QColor GetColorForType(Oid oid) const;
QColor explainTime[4];
QColor explainEstError[3];
};
const ColorTheme& GetColorTheme();
void InitApplicationPalette();

View file

@ -1,10 +1,14 @@
#include "util/PgLabItemDelegate.h"
#include <QApplication>
#include <QPainter>
#include "Pgsql_oids.h"
#include "ResultTableModelUtil.h"
#include "CustomDataRole.h"
#include "AbstractEditorFactory.h"
#include "Colors.h"
#include "utils/HumanReadableBytes.h"
#include "qstyleoption.h"
PgLabItemDelegate::PgLabItemDelegate(QObject *parent)
: QStyledItemDelegate(parent)
@ -82,6 +86,8 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
// }
// }
const ColorTheme &theme = GetColorTheme();
Oid oid = InvalidOid;
value = index.data(CustomDataTypeRole); // get OID
if (value.isValid())
@ -90,7 +96,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
value = index.data(CustomDataMeaningRole);
DataMeaning meaning = value.isValid()
? static_cast<DataMeaning>(value.toInt())
: DataMeaningNormal;
: DataMeaning::Normal;
value = index.data(Qt::DisplayRole);
@ -98,9 +104,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
if (value.isValid() && ! value.isNull()) {
QColor forground_color = oid == Pgsql::bool_oid
? GetDefaultBoolColor(value.toBool())
: GetDefaultColorForType(oid);
QColor forground_color;
option->features |= QStyleOptionViewItem::HasDisplay;
if (oid == Pgsql::bool_oid) {
@ -109,82 +113,93 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
option->text = FormatBoolForDisplay(b);
}
else {
forground_color = GetDefaultColorForType(oid);
if (meaning == DataMeaningBytes) {
QString suffix;
auto s = value.toLongLong();
double val;
if (s > 1024 * 1024 * 1000) {
val = s / (1024 * 1024 * 1024);
suffix = "GiB";
forground_color = QColorConstants::Svg::darkorange;
option->font.setBold(true);
}
else if (s > 1024 * 1000) {
val = s / (1024 * 1024);
suffix = "MiB";
forground_color = QColorConstants::Svg::darkgoldenrod;
}
else if (s > 1000) {
val = s / 1024;
suffix = "KiB";
forground_color = QColorConstants::Svg::darkgreen;
}
else {
val = s;
suffix = "B";
forground_color = QColorConstants::Svg::darkblue;
}
option->text = QString{ "%1 %2" }.arg(val, 3, 'g', -1 ).arg(suffix) ;
forground_color = theme.GetColorForType(oid);
if (meaning == DataMeaning::Bytes) {
option->text = HandleBytes(value.toLongLong(), forground_color);
}
else {
auto str = value.toString();
auto s = str.left(100);
// auto f = s.indexOf('\n');
// option->text = ((f > 0) ? s.left(f) : s).toString();
option->text = s;
}
}
option->palette.setBrush(QPalette::Text, QBrush(forground_color));
}
else {
option->palette.setBrush(QPalette::Text, QBrush(GetDefaultNullColor()));
option->palette.setBrush(QPalette::Text, QBrush(theme.nullColor));
option->features |= QStyleOptionViewItem::HasDisplay;
option->text = "null";
}
// if (option->state & QStyle::State_HasFocus) {
// QColor color = Qt::darkYellow;
// option->palette.setColor(QPalette::Active, QPalette::Highlight, color);
// // option->palette.setColor(QPalette::Active, QPalette::AlternateBase, color);
// option->backgroundBrush = QBrush(color);
// option->font.setWeight(QFont::ExtraBold);
// }
// option->backgroundBrush = qvariant_cast<QBrush>(index.data(Qt::BackgroundRole));
// disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
option->styleObject = nullptr;
}
void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
QString PgLabItemDelegate::HandleBytes(qlonglong s, QColor &forground_color) const
{
Q_ASSERT(index.isValid());
const ColorTheme& theme = GetColorTheme();
auto str = HumanReadableBytes(s);
if (s > 1024 * 1024 * 1024) {
forground_color = theme.gigaBytes;
}
else if (s > 1024 * 1024) {
forground_color = theme.megaBytes;
}
else if (s > 1024) {
forground_color = theme.kiloBytes;
}
else {
forground_color = theme.bytes;
}
return QString::fromStdString(str);
}
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget; // QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
const QWidget *widget = option.widget; // QStyledItemDelegatePrivate::widget(option);
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget);
if (option.state & QStyle::State_HasFocus) {
QColor color = Qt::red;
painter->setPen(QPen(QBrush(color), 2.0));
painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
}
}
QWidget *PgLabItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if (m_editorFactory)
return m_editorFactory->createEditor(parent, option, index);
return QStyledItemDelegate::createEditor(parent, option, index);
if (m_editorFactory)
return m_editorFactory->createEditor(parent, option, index);
return QStyledItemDelegate::createEditor(parent, option, index);
}
void PgLabItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
if (m_editorFactory)
m_editorFactory->setEditorData(editor, index);
if (m_editorFactory)
m_editorFactory->setEditorData(editor, index);
else
QStyledItemDelegate::setEditorData(editor, index);
}

View file

@ -29,7 +29,8 @@ protected:
virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;
private:
AbstractEditorFactory *m_editorFactory = nullptr;
AbstractEditorFactory *m_editorFactory = nullptr;
QString HandleBytes(qlonglong s, QColor &forground_color) const;
};
#endif // PGLABITEMDELEGATE_H

View file

@ -6,14 +6,19 @@
PgLabTableView::PgLabTableView(QWidget *parent)
: QTableView(parent)
{
setAlternatingRowColors(true);
setItemDelegate(new PgLabItemDelegate(this));
setWordWrap(false);
setAlternatingRowColors(true);
auto pal = palette();
pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
setPalette(pal);
setItemDelegate(new PgLabItemDelegate(this));
setWordWrap(false);
QPalette pal = palette();
//pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.color(QPalette::Active, QPalette::Highlight));
//pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.color(QPalette::Active, QPalette::HighlightedText));
// pal.setColor(QPalette::Active, QPalette::Base, QColor(20, 20, 20));
// pal.setColor(QPalette::Active, QPalette::AlternateBase, Qt::black);
// setPalette(pal);
auto vertical_header = verticalHeader();
vertical_header->setMinimumSectionSize(16);

View file

@ -1,35 +1,62 @@
#pragma once
#include <QTableWidget>
#include <QTreeView>
#include <QSortFilterProxyModel>
#include "util/PgLabItemDelegate.h"
#include "util/PgLabTableView.h"
#include <optional>
class PgDatabaseCatalog;
template <typename TableModel>
template <typename ViewType>
void ResizeColumnsToContent(ViewType *vt)
{
vt->resizeColumnsToContents();
}
template <>
inline void ResizeColumnsToContent<QTreeView>(QTreeView *)
{
}
template <typename ViewType>
void InitView(ViewType *vt)
{}
template <>
inline void InitView<QTreeView>(QTreeView *tv)
{
tv->setAlternatingRowColors(true);
tv->setItemDelegate(new PgLabItemDelegate(tv));
tv->setWordWrap(false);
}
template <typename TableModel, typename ViewType = PgLabTableView>
class PgLabTableViewHelper {
public:
PgLabTableViewHelper(QWidget * parent, TableModel *tableModel)
{
m_tableView = new PgLabTableView(parent);
m_itemView = new ViewType(parent);
InitView(m_itemView);
m_dataModel = tableModel;
m_sortFilter = new QSortFilterProxyModel(parent);
m_sortFilter->setSourceModel(m_dataModel);
m_tableView->setModel(m_sortFilter);
m_tableView->setSortingEnabled(true);
m_itemView->setModel(m_sortFilter);
m_itemView->setSortingEnabled(true);
m_sortFilter->sort(0, Qt::AscendingOrder);
}
PgLabTableViewHelper(QWidget * parent)
: PgLabTableViewHelper(parent, new TableModel(parent))
{}
PgLabTableView *tableView() const
ViewType *itemView() const
{
return m_tableView;
return m_itemView;
}
TableModel *dataModel() const
@ -45,27 +72,27 @@ public:
void setCatalog(std::shared_ptr<PgDatabaseCatalog> cat)
{
m_dataModel->setCatalog(cat);
m_tableView->resizeColumnsToContents();
ResizeColumnsToContent(m_itemView);
}
QModelIndex currentSourceIndex() const
{
QModelIndex index = m_tableView->selectionModel()->currentIndex();
QModelIndex index = m_itemView->selectionModel()->currentIndex();
if (!index.isValid())
return index;
return m_sortFilter->mapToSource(index);
}
typename TableModel::RowItem rowItemForSourceRow(int row) const
typename TableModel::RowItem rowItemForSourceRow(QModelIndex index) const
{
return m_dataModel->rowItem(row);
return m_dataModel->rowItem(index);
}
typename TableModel::RowItem rowItemForProxyIndex(QModelIndex index)
{
QModelIndex sourceIndex = m_sortFilter->mapToSource(index);
return m_dataModel->rowItem(sourceIndex.row());
return m_dataModel->rowItem(sourceIndex);
}
typename TableModel::RowItem rowItemForProxyRow(int row) const
@ -79,12 +106,12 @@ public:
if (!index.isValid())
return {};
return rowItemForSourceRow(index.row());
return rowItemForSourceRow(index);
}
private:
PgLabTableView *m_tableView = nullptr;
ViewType *m_itemView = nullptr;
TableModel *m_dataModel = nullptr;
QSortFilterProxyModel *m_sortFilter = nullptr;
};

View file

@ -2,25 +2,28 @@
#include "catalog/PgTypeContainer.h"
#include "SqlLexer.h"
#include "util/Colors.h"
SqlSyntaxHighlighter::SqlSyntaxHighlighter(QTextDocument *parent)
: QSyntaxHighlighter(parent)
{
m_keywordFormat.setForeground(QColor(32, 32, 192));
m_keywordFormat.setFontWeight(QFont::Bold);
const ColorTheme& theme = GetColorTheme();
m_commentFormat.setForeground(QColor(128, 128, 128));
m_keywordFormat.setForeground(theme.keyword);
m_keywordFormat.setFontWeight(QFont::Bold);
m_quotedStringFormat.setForeground(QColor(192, 32, 192));
m_commentFormat.setForeground(theme.comment);
m_quotedIdentifierFormat.setForeground(QColor(192, 128, 32));
m_quotedStringFormat.setForeground(theme.quotedString);
m_typeFormat.setForeground(QColor(32, 192, 32));
m_quotedIdentifierFormat.setForeground(theme.quotedIdentifier);
m_typeFormat.setForeground(theme.type);
m_typeFormat.setFontWeight(QFont::Bold);
m_parameterFormat.setForeground(QColor(192, 32, 32));
m_parameterFormat.setForeground(theme.parameter);
m_parameterFormat.setFontWeight(QFont::Bold);
}

View file

@ -1,481 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.9.2, 2019-09-15T08:19:54. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{78888978-f53a-469c-ac73-a847c4fa88dd}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">2</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">0</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey">
<value type="QString">-fno-delayed-template-parsing</value>
</valuelist>
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
<valuelist type="QVariantList" key="ClangStaticAnalyzer.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">false</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.TidyAndClazy</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles">
<value type="QString">X:/Prog/pglab/pglab/ConnectionManagerWindow.cpp</value>
<value type="QString">X:/Prog/pglab/pglab/ConnectionConfigurationWidget.cpp</value>
<value type="QString">X:/Prog/pglab/pglab/ConnectionController.cpp</value>
<value type="QString">X:/Prog/pglab/pglab/ConnectionListModel.cpp</value>
</valuelist>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop Qt 5.13.0 MSVC2017 32bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop Qt 5.13.0 MSVC2017 32bit</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">qt.qt5.5130.win32_msvc2017_kit</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">true</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.BuildTargets"/>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
<value type="bool" key="Qt4ProjectManager.MakeStep.OverrideMakeflags">false</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy Configuration</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">pglab</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/pglab/pglab.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/pglab</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.1">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">pglabtests</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/tests/pglabtests/pglabtests.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/tests/pglabtests</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.2">
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
<value type="QString">cpu-cycles</value>
</valuelist>
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
<value type="int" key="Analyzer.Perf.Frequency">250</value>
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">PgsqlTests</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/tests/PgsqlTests/PgsqlTests.pro</value>
<value type="QString" key="RunConfiguration.Arguments"></value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseLibrarySearchPath">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
<value type="QString" key="RunConfiguration.WorkingDirectory"></value>
<value type="QString" key="RunConfiguration.WorkingDirectory.default">X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/tests/PgsqlTests</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">3</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">21</value>
</data>
<data>
<variable>Version</variable>
<value type="int">21</value>
</data>
</qtcreator>

View file

@ -81,7 +81,7 @@ private:
void doStateCallback(ASyncDBConnection::StateData state);
/// Wait's for a command to come in and send's it to the server
void waitForAndSendCommand();
bool waitForAndSendCommand();
void doNewCommand();
void waitForResult();
@ -236,7 +236,9 @@ void ASyncDBConnectionThread::communicate()
if (m_state == ASyncDBConnection::State::Connected) {
waitForAndSendCommand();
if (!waitForAndSendCommand()) {
return;
}
}
else if (m_state == ASyncDBConnection::State::QuerySend || m_state == ASyncDBConnection::State::CancelSend) {
// Wait for result, even after a cancel we should wait, for all results
@ -255,15 +257,22 @@ void ASyncDBConnectionThread::stop()
void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state)
{
qDebug() << "State change " + state.Message;
m_state = state.State;
Q_EMIT asyncConnObject->onStateChanged(state);
Q_EMIT asyncConnObject->onStateChanged();
}
void ASyncDBConnectionThread::waitForAndSendCommand()
bool ASyncDBConnectionThread::waitForAndSendCommand()
{
SOCKET sock = static_cast<SOCKET>(m_connection.socket());
Win32Event socket_event(Win32Event::Reset::Manual, Win32Event::Initial::Clear);
long fd = FD_READ | FD_CLOSE;
WSAEventSelect(sock, socket_event.handle(), fd);
WaitHandleList whl;
auto wait_result_new_command = whl.add(m_commandQueue.m_newEvent);
auto wait_result_stop = whl.add(m_stopEvent);
auto wait_result_socket = whl.add(socket_event);
whl.add(m_stopEvent);
DWORD res = MsgWaitForMultipleObjectsEx(
whl.count(), // _In_ DWORD nCount,
@ -275,8 +284,17 @@ void ASyncDBConnectionThread::waitForAndSendCommand()
if (res == wait_result_new_command) {
doNewCommand();
}
else if (res == wait_result_socket) {
WSANETWORKEVENTS net_events;
WSAEnumNetworkEvents(sock, socket_event.handle(), &net_events);
if (net_events.lNetworkEvents & FD_CLOSE) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection lost" });
return false;
}
}
// Note if it was stop we can just return and function
// above will stop looping because terminateRequested has been set too by stop
return true;
}
void ASyncDBConnectionThread::doNewCommand()
@ -284,7 +302,6 @@ void ASyncDBConnectionThread::doNewCommand()
// get command from top of queue (but leave it in the queue, we need the callback)
if (! m_commandQueue.m_queue.empty()) {
const Command &command = m_commandQueue.m_queue.front();
bool query_send = false;
if (command.params.empty())
m_connection.sendQuery(command.command.c_str());
else
@ -359,6 +376,11 @@ void ASyncDBConnectionThread::waitForResult()
finished = true;
}
}
else if (net_events.lNetworkEvents & FD_CLOSE) {
doStateCallback({ ASyncDBConnection::State::NotConnected, "Close while waiting for result" });
finished = true;
stop();
}
}
if (res == wait_result_stop) {
// Send cancel, close connection and terminate thread
@ -413,7 +435,7 @@ void ASyncDBConnection::doStateCallback(State state)
m_connection.setNoticeReceiver(
[this](const PGresult *result) { processNotice(result); });
}
Q_EMIT onStateChanged(state);
Q_EMIT onStateChanged();
}

View file

@ -63,7 +63,7 @@ public:
bool cancel();
Q_SIGNALS:
void onStateChanged(ASyncDBConnection::StateData state);
void onStateChanged();
void onNotice(Pgsql::ErrorDetails notice);
private:

View file

@ -19,10 +19,24 @@ namespace {
{ SslMode::verify_full, "verify-full" }
};
inline const char *valuePtr(const std::string &v)
{
return v.empty() ? nullptr : v.c_str();
}
// inline const char *valuePtr(const std::string &v)
// {
// return v.empty() ? nullptr : v.c_str();
// }
struct {
const char * host = "host";
const char * hostaddr = "hostaddr";
const char * port = "port";
const char * dbname = "dbname";
const char * user = "user";
const char * password = "password";
const char * sslmode = "sslmode";
const char * sslcert = "sslcert";
const char * sslkey = "sslkey";
const char * sslrootcert = "sslrootcert";
const char * sslcrl = "sslcrl";
} keywords;
} // end unnamed namespace
@ -32,21 +46,20 @@ QString SslModeToString(SslMode sm)
if (e.mode == sm)
return QString::fromUtf8(e.string);
return {};
return {};
}
SslMode StringToSslMode(QString s)
{
SslMode result = SslMode::allow;
for (auto e : SslModeStringTable)
if (e.string == s)
result = e.mode;
return e.mode;
return {};
return SslMode::allow;
}
ConnectionConfig::ConnectionConfig()
: m_applicationName(QCoreApplication::applicationName().toUtf8().data())
: m_parameters{{"application_name", QCoreApplication::applicationName()}}
{}
const ConnectionGroup *ConnectionConfig::parent() const
@ -92,168 +105,131 @@ const QString& ConnectionConfig::name() const
void ConnectionConfig::setHost(const QString& host)
{
if (m_host != host)
{
m_dirty = true;
m_host = std::move(host);
}
setParameter(keywords.host, host);
}
const QString& ConnectionConfig::host() const
QString ConnectionConfig::host() const
{
return m_host;
return getParameter(keywords.host);
}
void ConnectionConfig::setHostAddr(const QString &v)
{
if (m_hostaddr != v)
{
m_dirty = true;
m_hostaddr = std::move(v);
}
setParameter(keywords.hostaddr, v);
}
const QString& ConnectionConfig::hostAddr() const
QString ConnectionConfig::hostAddr() const
{
return m_hostaddr;
return getParameter(keywords.hostaddr);
}
void ConnectionConfig::setPort(unsigned short port)
{
if (m_port != port)
{
m_dirty = true;
m_port = port;
}
setParameter(keywords.port, QString::number(port));
}
unsigned short ConnectionConfig::port() const
{
return m_port;
QString s = getParameter(keywords.port);
if (s.isEmpty())
return 5432;
unsigned short port = static_cast<unsigned short>(s.toInt());
return port;
}
void ConnectionConfig::setUser(const QString& v)
{
if (m_user != v)
{
m_dirty = true;
m_user = v;
}
setParameter(keywords.user, v);
}
const QString& ConnectionConfig::user() const
QString ConnectionConfig::user() const
{
return m_user;
return getParameter(keywords.user);
}
void ConnectionConfig::setPassword(const QString& v)
{
if (m_password != v)
{
m_dirty = true;
m_password = v;
}
setParameter(keywords.password, v);
}
const QString& ConnectionConfig::password() const
QString ConnectionConfig::password() const
{
return m_password;
return getParameter(keywords.password);
}
void ConnectionConfig::setDbname(const QString& v)
{
if (m_dbname != v)
{
m_dirty = true;
m_dbname = v;
}
setParameter(keywords.dbname, v);
}
const QString& ConnectionConfig::dbname() const
QString ConnectionConfig::dbname() const
{
return m_dbname;
return getParameter(keywords.dbname);
}
void ConnectionConfig::setSslMode(SslMode m)
{
if (m_sslMode != m)
{
m_dirty = true;
m_sslMode = m;
}
setParameter(keywords.sslmode, SslModeToString(m));
}
SslMode ConnectionConfig::sslMode() const
{
return m_sslMode;
QString s = getParameter(keywords.sslmode);
return StringToSslMode(s);
}
void ConnectionConfig::setSslCert(const QString& v)
{
if (m_sslCert != v)
{
m_dirty = true;
m_sslCert = std::move(v);
}
setParameter(keywords.sslcert, v);
}
const QString& ConnectionConfig::sslCert() const
QString ConnectionConfig::sslCert() const
{
return m_sslCert;
return getParameter(keywords.sslcert);
}
void ConnectionConfig::setSslKey(const QString& v)
{
if (m_sslKey != v)
{
m_dirty = true;
m_sslKey = std::move(v);
}
setParameter(keywords.sslkey, v);
}
const QString& ConnectionConfig::sslKey() const
QString ConnectionConfig::sslKey() const
{
return m_sslKey;
return getParameter(keywords.sslkey);
}
void ConnectionConfig::setSslRootCert(const QString& v)
{
if (m_sslRootCert != v)
{
m_dirty = true;
m_sslRootCert = std::move(v);
}
setParameter(keywords.sslrootcert, v);
}
const QString& ConnectionConfig::sslRootCert() const
QString ConnectionConfig::sslRootCert() const
{
return m_sslRootCert;
return getParameter(keywords.sslrootcert);
}
void ConnectionConfig::setSslCrl(const QString& v)
{
if (m_sslCrl != v)
{
m_dirty = true;
m_sslCrl = std::move(v);
}
setParameter(keywords.sslcrl, v);
}
const QString& ConnectionConfig::sslCrl() const
QString ConnectionConfig::sslCrl() const
{
return m_sslCrl;
return getParameter(keywords.sslcrl);
}
bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
{
return m_host == rhs.m_host
&& m_hostaddr == rhs.m_hostaddr
&& m_port == rhs.m_port
&& m_user == rhs.m_user
&& m_password == rhs.m_password
&& m_dbname == rhs.m_dbname;
}
// bool ConnectionConfig::isSameDatabase(const ConnectionConfig &rhs) const
// {
// return host() == rhs.host()
// // && m_hostaddr == rhs.m_hostaddr
// // && m_port == rhs.m_port
// // && m_user == rhs.m_user
// // && m_password == rhs.m_password
// // && m_dbname == rhs.m_dbname
// ;
// }
bool ConnectionConfig::dirty() const
{
@ -272,7 +248,7 @@ QString ConnectionConfig::makeLongDescription() const
return result;
}
QByteArray ConnectionConfig::encodedPassword() const
const QByteArray& ConnectionConfig::encodedPassword() const
{
return m_encodedPassword;
}
@ -314,58 +290,76 @@ QString ConnectionConfig::escapeConnectionStringValue(const QString &value)
QString ConnectionConfig::connectionString() const
{
QString s;
s += "host="
% escapeConnectionStringValue(m_host)
% " port="
% QString::number(m_port)
% " user="
% escapeConnectionStringValue(m_user);
s += " password=";
s += escapeConnectionStringValue(m_password);
s += " dbname=";
s += escapeConnectionStringValue(m_dbname);
s += " sslmode=";
s += SslModeToString(m_sslMode);
if (!m_sslCert.isEmpty())
{
s += " sslcert=";
s += escapeConnectionStringValue(m_sslCert);
}
if (!m_sslKey.isEmpty())
{
s += " sslkey=";
s += escapeConnectionStringValue(m_sslKey);
}
if (!m_sslRootCert.isEmpty())
{
s += " sslrootcrt=";
s += escapeConnectionStringValue(m_sslRootCert);
}
if (!m_sslCrl.isEmpty())
{
s += " sslCrl=";
s += escapeConnectionStringValue(m_sslCrl);
}
s += " client_encoding=utf8";
s += " application_name=";
s += escapeConnectionStringValue(m_applicationName);
return s;
for (auto && param : m_parameters)
{
// maybe we should prevent empty parameters from staying in the map?
if (!param.second.isEmpty())
{
if (!s.isEmpty())
s += " ";
s += param.first % "=" % escapeConnectionStringValue(param.second);
}
}
return s;
}
void ConnectionConfig::setParameter(const QString &name, const QString &value)
{
if (value.isEmpty())
{
if (m_parameters.erase(name) > 0)
{
m_dirty = true;
}
}
else
{
auto findResult = m_parameters.find(name);
if (findResult == m_parameters.end())
{
m_parameters.insert({name, value});
m_dirty = true;
}
else if (findResult->second != value)
{
findResult->second = value;
m_dirty = true;
}
}
//if (name == "sslMode")
// m_sslMode = StringToSslMode(value);
// Would it better to store everything in the map or keep the specific
// fields for common keywords?
// Map over fields
// + can use foreach
// - not strongly typed, but we should be carefull not to restrict ourselves
// the specific fields are more something that helps in the UI
}
QString ConnectionConfig::getParameter(const QString &name) const
{
auto findResult = m_parameters.find(name);
if (findResult == m_parameters.end())
return {};
return findResult->second;
}
void ConnectionConfig::writeToEnvironment(QProcessEnvironment &env) const
{
strToEnv(env, "PGHOST", m_host);
strToEnv(env, "PGHOSTADDR", m_hostaddr);
strToEnv(env, "PGPORT", QString::number(m_port));
strToEnv(env, "PGDATABASE", m_dbname);
strToEnv(env, "PGUSER", m_user);
strToEnv(env, "PGPASSWORD", m_password);
strToEnv(env, "PGSSLMODE", SslModeToString(m_sslMode));
strToEnv(env, "PGSSLCERT", m_sslCert);
strToEnv(env, "PGSSLKEY", m_sslKey);
strToEnv(env, "PGSSLROOTCERT", m_sslRootCert);
strToEnv(env, "PGSSLCRL", m_sslCrl);
strToEnv(env, "PGHOST", getParameter(keywords.host));
strToEnv(env, "PGHOSTADDR", getParameter(keywords.hostaddr));
strToEnv(env, "PGPORT", getParameter(keywords.port));
strToEnv(env, "PGDATABASE", getParameter(keywords.dbname));
strToEnv(env, "PGUSER", getParameter(keywords.user));
strToEnv(env, "PGPASSWORD", getParameter(keywords.password));
strToEnv(env, "PGSSLMODE", getParameter(keywords.sslmode));
// strToEnv(env, "PGSSLCERT", m_sslCert);
// strToEnv(env, "PGSSLKEY", m_sslKey);
// strToEnv(env, "PGSSLROOTCERT", m_sslRootCert);
// strToEnv(env, "PGSSLCRL", m_sslCrl);
strToEnv(env, "PGSSLCOMPRESSION", "0");
strToEnv(env, "PGCONNECT_TIMEOUT", "10");
env.insert("PGCLIENTENCODING", "utf8");

View file

@ -6,6 +6,7 @@
#include <QMetaType>
#include <QUuid>
#include <QVector>
#include <map>
#include <vector>
#include <string>
@ -45,6 +46,7 @@ public:
using Connections = QVector<std::shared_ptr<ConnectionConfig>>;
const Connections& connections() const { return m_connections; }
Connections& connections() { return m_connections; }
void erase(int idx, int count = 1);
/// Adds cc to the group and returns the index within the group.
@ -73,42 +75,42 @@ public:
const QString& name() const;
void setHost(const QString& host);
const QString& host() const;
QString host() const;
void setHostAddr(const QString& v);
const QString& hostAddr() const;
QString hostAddr() const;
void setPort(unsigned short port);
unsigned short port() const;
void setUser(const QString& v);
const QString& user() const;
QString user() const;
void setPassword(const QString& v);
const QString& password() const;
QString password() const;
void setDbname(const QString& v);
const QString& dbname() const;
QString dbname() const;
void setSslMode(SslMode m);
SslMode sslMode() const;
void setSslCert(const QString& v);
const QString& sslCert() const;
void setSslCert(const QString& v);
QString sslCert() const;
void setSslKey(const QString& v);
const QString& sslKey() const;
void setSslKey(const QString& v);
QString sslKey() const;
void setSslRootCert(const QString& v);
const QString& sslRootCert() const;
void setSslRootCert(const QString& v);
QString sslRootCert() const;
void setSslCrl(const QString& v);
const QString& sslCrl() const;
void setSslCrl(const QString& v);
QString sslCrl() const;
// const char * const * getKeywords() const;
// const char * const * getValues() const;
bool isSameDatabase(const ConnectionConfig &rhs) const;
// bool isSameDatabase(const ConnectionConfig &rhs) const;
void writeToEnvironment(QProcessEnvironment &env) const;
@ -118,7 +120,7 @@ public:
bool operator==(QUuid id) const { return m_uuid == id; }
QString makeLongDescription() const;
QByteArray encodedPassword() const;
const QByteArray& encodedPassword() const;
void setEncodedPassword(const QByteArray &encodedPassword);
// void write(QDataStream &out) const;
@ -130,26 +132,35 @@ public:
*/
static QString escapeConnectionStringValue(const QString &value);
QString connectionString() const;
void setParameter(const QString &name, const QString &value);
QString getParameter(const QString &name) const;
const std::unordered_map<QString, QString>& getParameters() const
{
return m_parameters;
}
private:
QUuid m_uuid;
QString m_name;
QString m_host;
QString m_hostaddr;
uint16_t m_port = 5432;
QString m_name;
// QString m_host;
// QString m_hostaddr;
// uint16_t m_port = 5432;
QString m_user;
QString m_password; ///< Note this is not saved in the DB only the m_encodedPassword is safed.
QString m_dbname;
// QString m_user;
// QString m_password; ///< Note this is not saved in the DB only the m_encodedPassword is safed.
// QString m_dbname;
SslMode m_sslMode = SslMode::prefer;
QString m_sslCert;
QString m_sslKey;
QString m_sslRootCert;
QString m_sslCrl;
// SslMode m_sslMode = SslMode::prefer;
// QString m_sslCert;
// QString m_sslKey;
// QString m_sslRootCert;
// QString m_sslCrl;
QString m_applicationName;
// QString m_applicationName;
QByteArray m_encodedPassword;
std::unordered_map<QString, QString> m_parameters;
bool m_dirty = false;
ConnectionGroup* m_group = nullptr;

View file

@ -8,12 +8,12 @@ Json::Value ParamToJson(const Param &param)
return v;
}
Param ParamFromJson(const Json::Value &json)
{
Param p;
// Param ParamFromJson(const Json::Value &json)
// {
// Param p;
return p;
}
// return p;
// }
Json::Value ParamListToJson(const t_ParamList &list)
{
@ -24,14 +24,15 @@ Json::Value ParamListToJson(const t_ParamList &list)
return root;
}
t_ParamList ParamListFromJson(const Json::Value &json)
{
t_ParamList result;
if (json.isArray()) {
result.reserve(json.size());
for (auto &e : json) {
result.push_back(ParamFromJson(e));
}
}
return result;
}
// t_ParamList ParamListFromJson(const Json::Value &json)
// {
// t_ParamList result;
// if (json.isArray()) {
// result.reserve(json.size());
// for (auto &e : json) {
// result.push_back(ParamFromJson(e));
// }
// }
// return result;
//}

View file

@ -41,7 +41,7 @@ QVariant ParamListModel::headerData(int section, Qt::Orientation orientation, in
int ParamListModel::rowCount(const QModelIndex &) const
{
return m_paramList.size();
return static_cast<int>(m_paramList.size());
}
int ParamListModel::columnCount(const QModelIndex &) const

View file

@ -82,16 +82,6 @@ QString PgAcl::singleString() const
void operator<<(PgAcl &acl, const Pgsql::Value &v)
{
acl.setFromString(v);
// const char *c = v.c_str();
// Pgsql::ArrayParser parser(c);
// while (true) {
// auto elem = parser.GetNextElem();
// if (!elem.ok)
// break;
// auto&& v = elem.value.value_or("");
// s.emplace_back(QString(v.data()));
// }
}
namespace Pgsql {

View file

@ -3,18 +3,43 @@
#include "SqlFormattingUtils.h"
#include "PgClass.h"
#include "PgDatabaseCatalog.h"
#include "PgSequenceContainer.h"
#include "PgTypeContainer.h"
#include "PgCollation.h"
#include "PgCollationContainer.h"
#include "Pgsql_oids.h"
PgAttribute::Identity PgAttribute::getIdentity() const
{
switch (identity)
{
case '\0': return Identity::None;
case 'a': return Identity::Always;
case 'd': return Identity::ByDefault;
}
assert(false); // we shouldn't get here
return {};
}
PgAttribute::Generated PgAttribute::getGenerated() const
{
switch (generated)
{
case '\0': return Generated::None;
case 's': return Generated::Stored;
}
assert(false); // we shouldn't get here
return {};
}
QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
{
// create: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// alter: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// constraints NULL/NOT NULL, DEFAULT, GENERATED other constraints will be ignored here a
QString type_str;
if (isSerial()) {
if (isSerial() && identity == '\0') {
if (typid == Pgsql::int4_oid)
type_str = "SERIAL";
else if (typid == Pgsql::int8_oid)
@ -35,16 +60,31 @@ QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
if (notnull)
sql += " NOT NULL";
if (hasdef && !isSerial())
sql += " DEFAULT " % defaultValue;
if (identity != '\0') {
auto identity = getIdentity();
auto generated = getGenerated();
if (identity != Identity::None) {
sql += " GENERATED ";
if (identity == 'a') sql += "ALWAYS";
else if (identity == 'd') sql += "BY DEFAULT";
if (identity == Identity::Always)
sql += "ALWAYS";
else if (identity == Identity::ByDefault)
sql += "BY DEFAULT";
sql += " AS IDENTITY";
auto sequence = cat.sequences()->getByObjectNsAndName(serschema, sername);
auto options_string = sequence->nonDefaultOptionsString();
if (!options_string.isEmpty())
sql += "(" % options_string % ")";
}
// TODO sequence options might be missing
else if (generated != Generated::None)
{
if (generated == Generated::Stored)
sql += " GENERATED ALWAYS AS " % defaultValue % " STORED";
}
else
{
if (hasdef && !isSerial())
sql += " DEFAULT " % defaultValue;
}
return sql;
}

View file

@ -11,7 +11,29 @@ class PgDatabaseCatalog;
class PgAttribute {
public:
using Key = std::tuple<Oid, int16_t>;
enum class Identity {
None,
Always,
ByDefault
};
enum class Generated {
None,
Stored
};
class Key {
public:
Key() = default;
Key(Oid relationId, int16_t num)
: RelationId(relationId)
, Num(num)
{}
std::strong_ordering operator <=> (const Key &rhs) const = default;
private:
Oid RelationId = InvalidOid;
int16_t Num = 0;
};
Oid relid = InvalidOid;
QString name;
@ -23,19 +45,36 @@ public:
bool notnull = false;
bool hasdef = false;
char identity = '\0';
char generated = '\0';
bool isdropped = false;
bool islocal = true;
Oid collation = InvalidOid;
QString acl;
QString options;
Identity getIdentity() const;
Generated getGenerated() const;
QString defaultValue; ///< Comes from pg_attrdef table
QString sername, serschema; // serial sequence name and schema
QString description; ///< from pg_description
bool operator==(Key _k) const { return relid == std::get<0>(_k) && num == std::get<1>(_k); }
Key key() const
{
return Key(relid, num);
}
std::strong_ordering operator <=> (const Key& rhs) const
{
return key() <=> rhs;
}
bool operator==(const Key &k) const
{
return key() == k;
}
bool operator==(const QString &n) const { return name == n; }
bool operator<(Key _k) const { return relid < std::get<0>(_k) || (relid == std::get<0>(_k) && num < std::get<1>(_k)); }
bool operator<(const PgAttribute &rhs) const { return relid < rhs.relid || (relid == rhs.relid && num < rhs.num); }
/// Return the part of the SQL create statement that can be reused for both the CREATE TABLE and ALTER TABLE ADD COLUMN

View file

@ -16,12 +16,14 @@ SELECT attrelid, attname, atttypid, attstattarget,
cs.relname AS sername, ns.nspname AS serschema, d.description)__";
if (m_catalog.serverVersion() >= 100000)
q += ", attidentity";
q += R"__(
if (m_catalog.serverVersion() >= 120000)
q += ", attgenerated";
q += R"__(
FROM pg_catalog.pg_attribute AS att
LEFT JOIN pg_attrdef AS def ON attrelid=adrelid AND attnum=adnum
LEFT JOIN (pg_depend JOIN pg_class cs ON classid='pg_class'::regclass AND objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
LEFT JOIN pg_namespace ns ON ns.oid=cs.relnamespace
LEFT JOIN pg_catalog.pg_description AS d ON (objoid=attrelid AND d.objsubid=attnum))__";
LEFT JOIN pg_catalog.pg_description AS d ON (objoid=attrelid AND d.objsubid=attnum) )__";
return q;
}
@ -36,6 +38,8 @@ PgAttribute PgAttributeContainer::loadElem(const Pgsql::Row &row)
>> v.sername >> v.serschema >> v.description;
if (m_catalog.serverVersion() >= 100000)
col >> v.identity;
if (m_catalog.serverVersion() >= 120000)
col >> v.generated;
return v;
}

View file

@ -1,4 +1,5 @@
#include "PgClass.h"
#include "PgAttribute.h"
#include "PgAttributeContainer.h"
#include "PgClassContainer.h"
#include "PgDatabaseCatalog.h"
@ -6,7 +7,8 @@
#include "PgInheritsContainer.h"
#include <QStringBuilder>
#include "SqlFormattingUtils.h"
#include <ranges>
#include <cassert>
void operator<<(RelPersistence &s, const Pgsql::Value &v)
@ -33,33 +35,60 @@ void operator<<(RelKind &s, const Pgsql::Value &v)
const char *c = v.c_str();
switch (*c)
{
case 'r':
s = RelKind::Table;
break;
case 'i':
s = RelKind::Index;
break;
case 'S':
s = RelKind::Sequence;
break;
case 'v':
s = RelKind::View;
break;
case 'm':
s = RelKind::MaterializedView;
break;
case 'c':
s = RelKind::Composite;
break;
case 't':
s = RelKind::Toast;
break;
case 'f':
s = RelKind::ForeignTable;
break;
case 'r':
s = RelKind::Table;
break;
case 'i':
s = RelKind::Index;
break;
case 'S':
s = RelKind::Sequence;
break;
case 'v':
s = RelKind::View;
break;
case 'm':
s = RelKind::MaterializedView;
break;
case 'c':
s = RelKind::Composite;
break;
case 't':
s = RelKind::Toast;
break;
case 'f':
s = RelKind::ForeignTable;
break;
case 'p':
s = RelKind::PartitionedTable;
break;
case 'I':
s = RelKind::PartitionedIndex;
break;
default:
throw std::runtime_error("Unknown RelKind");
}
}
void operator<<(PartitioningStrategy &s, const Pgsql::Value &v)
{
const char *c = v.c_str();
switch (*c)
{
case 'h':
s = PartitioningStrategy::Hash;
break;
case 'l':
s = PartitioningStrategy::List;
break;
case 'r':
s = PartitioningStrategy::Range;
break;
default:
throw std::runtime_error("Unknown PartitioningStrategy");
}
}
QString PgClass::createSql() const
{
if (createSqlCache.isEmpty())
@ -67,6 +96,7 @@ QString PgClass::createSql() const
switch (kind)
{
case RelKind::Table:
case RelKind::PartitionedTable:
createSqlCache = createTableSql();
break;
case RelKind::View:
@ -81,14 +111,16 @@ QString PgClass::typeName() const
{
switch (kind)
{
case RelKind::Table: return "TABLE";
case RelKind::Index: return "INDEX";
case RelKind::Sequence: return "SEQUENCE";
case RelKind::View: return "VIEW";
case RelKind::MaterializedView: return "MATERIALIZED VIEW";
case RelKind::Composite: return "COMPOSITE";
case RelKind::Toast: return "TOAST";
case RelKind::ForeignTable: return "FOREIGN TABLE";
case RelKind::Table: return "TABLE";
case RelKind::Index: return "INDEX";
case RelKind::Sequence: return "SEQUENCE";
case RelKind::View: return "VIEW";
case RelKind::MaterializedView: return "MATERIALIZED VIEW";
case RelKind::Composite: return "COMPOSITE";
case RelKind::Toast: return "TOAST";
case RelKind::ForeignTable: return "FOREIGN TABLE";
case RelKind::PartitionedTable: return "PARTITIONED TABLE";
case RelKind::PartitionedIndex: return "PARTITIONED INDEX";
}
throw std::runtime_error("Unexpected value in PgClass::typeName()");
}
@ -101,7 +133,18 @@ QString PgClass::aclAllPattern() const
default:
break;
}
return {};
return {};
}
QString PgClass::ddlTypeName() const
{
switch (kind)
{
case RelKind::PartitionedTable:
return "TABLE";
default:
return PgNamespaceObject::ddlTypeName();
}
}
QString PgClass::createTableSql() const
@ -115,52 +158,92 @@ QString PgClass::createTableSql() const
sql += "TEMP ";
sql += "TABLE ";
sql += fullyQualifiedQuotedObjectName();
sql += " (\n ";
auto && cols = catalog().attributes()->getColumnsForRelation(oid());
bool first = true;
for (auto && col : cols)
if (!partitionBoundaries.isEmpty())
{
if (col.num > 0 && !col.isdropped)
{
if (first)
{
first = false;
}
else
sql += ",\n ";
if (!col.islocal) sql += "-- ";
sql += col.columnDefinition(catalog());
}
// { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
// | table_constraint
// ] )
}
auto && constraints = catalog().constraints()->getConstraintsForRelation(oid());
for (auto && constraint: constraints)
{
if (first)
{
sql += "\n ";
first = false;
}
else
sql += ",\n ";
sql += getConstraintDefinition(catalog(), constraint, " ");
}
sql += " PARTITION OF " + getPartitionOfName();
sql += generateBodySql(true);
}
else
sql += generateBodySql(false);
sql += "\n)"
% generateInheritsSql()
// [ PARTITION BY { RANGE | LIST } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
// [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
// [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
sql += generateInheritsSql()
% partitionBySql()
% generateTablespaceSql()
% ";\n";
return sql;
}
QString PgClass::generateBodySql(bool isPartition) const
{
// - also remove commented inherited column list? They are listed in the column view no need for them in sql
// - mark them in the view as inherited?
// - detect when body empty and leave it out completely (only for partitions, is leaving it out always legal?)
// - need to detect "inherited" constraint because these should not be listed either
auto colsFilter = isPartition ?
[] (const PgAttribute &c)
{
return c.num > 0 // ignore system columns
&& !c.isdropped // ignore dropped columns
&& c.islocal;
}
:
[] (const PgAttribute &c)
{
return c.num > 0 // ignore system columns
&& !c.isdropped; // ignore dropped columns
}
;
auto && cols = catalog().attributes()->getColumnsForRelation(oid())
| std::views::filter(colsFilter);
auto && constraints = catalog().constraints()->getConstraintsForRelation(oid())
| std::views::filter([] (const auto &c) { return c.islocal; });
if (cols.empty() && constraints.empty())
return {};
QString sql = " (\n ";
bool first = true;
for (auto && col : cols)
{
if (first)
{
first = false;
}
else
sql += ",\n ";
if (!col.islocal) sql += "-- ";
sql += col.columnDefinition(catalog());
}
for (auto && constraint: constraints)
{
if (first)
{
sql += "\n ";
first = false;
}
else
sql += ",\n ";
sql += getConstraintDefinition(catalog(), constraint, " ");
}
sql += "\n)";
return sql;
}
QString PgClass::generateInheritsSql() const
{
if (!partitionBoundaries.isEmpty())
return "\n" + partitionBoundaries;
QString sql;
// [ INHERITS ( parent_table [, ... ] ) ]
auto parents = catalog().inherits()->getParentsOf(oid());
@ -179,6 +262,59 @@ QString PgClass::generateInheritsSql() const
return sql;
}
QString PgClass::partitionBySql() const
{
if (kind != RelKind::PartitionedTable)
return {};
QString sql = "\nPARTITION BY " % PartitionStrategyKeyword(partitionedTable.strategy);
sql += partitionKeySql();
return sql;
}
QString PgClass::partitionKeySql() const
{
QString result;
result += "(";
auto keyItem = partitionedTable.keyColumns.begin();
if (keyItem != partitionedTable.keyColumns.end())
{
result += partitionKeyItemSql(*keyItem);
for (++keyItem; keyItem != partitionedTable.keyColumns.end(); ++keyItem)
result += ", " % partitionKeyItemSql(*keyItem);
}
result += ")";
return result;
}
QString PgClass::partitionKeyItemSql(
const PartitioningKeyItem &keyItem
) const
{
if (keyItem.attNum == 0)
return keyItem.expression;
const PgAttribute *col = catalog().attributes()->findIf(
[this, &keyItem] (const auto &att)
{
return att.relid == oid() && att.num == keyItem.attNum;
}
);
assert(col != nullptr);
return quoteIdent(col->name);
}
QString PgClass::getPartitionOfName() const
{
auto parents = catalog().inherits()->getParentsOf(oid());
if (!parents.empty())
{
return catalog().classes()->getByKey(parents.front())->fullyQualifiedQuotedObjectName();
}
throw std::logic_error("Should only be called if there is a parent table");
}
QString PgClass::generateTablespaceSql() const
{
if (tablespace != 0)
@ -205,4 +341,15 @@ QString PgClass::createViewSql() const
return sql;
}
QString PartitionStrategyKeyword(PartitioningStrategy ps)
{
switch (ps) {
case PartitioningStrategy::Hash:
return "HASH";
case PartitioningStrategy::List:
return "LIST";
case PartitioningStrategy::Range:
return "RANGE";
}
throw std::runtime_error("Unknown PartitioningStrategy");
}

View file

@ -5,6 +5,9 @@
#include "PgNamespaceObject.h"
#include <QString>
#include <libpq-fe.h>
#include <boost/container/small_vector.hpp>
class PgAttribute;
enum class RelPersistence {
Permanent, // p
@ -14,7 +17,8 @@ enum class RelPersistence {
void operator<<(RelPersistence &s, const Pgsql::Value &v);
enum class RelKind {
enum class RelKind
{
Table, // r
Index, // i
Sequence, // S
@ -22,11 +26,44 @@ enum class RelKind {
MaterializedView, // m
Composite, // c
Toast, // t
ForeignTable // f
ForeignTable, // f
PartitionedTable, // p
PartitionedIndex // I
};
void operator<<(RelKind &s, const Pgsql::Value &v);
enum class PartitioningStrategy
{
Hash, // h
List, // l
Range // r
};
void operator<<(PartitioningStrategy &s, const Pgsql::Value &v);
QString PartitionStrategyKeyword(PartitioningStrategy ps);
class PartitioningKeyItem
{
public:
int16_t attNum;
Oid opClass;
Oid collation;
// expre nodetree
QString expression; // pg_get_expr(pg_node_tree, relation_oid)
};
using PartitioningKeyItems = boost::container::small_vector<PartitioningKeyItem, 3>;
class PgPartitionedTable
{
public:
PartitioningStrategy strategy;
Oid defaultPartition;
PartitioningKeyItems keyColumns;
};
class PgClass: public PgNamespaceObject {
public:
@ -47,6 +84,8 @@ public:
int minmxid;
std::vector<QString> options;
QString viewdef;
QString partitionBoundaries;
PgPartitionedTable partitionedTable; // ignore if RelKind != PartitionedTable
using PgNamespaceObject::PgNamespaceObject;
@ -55,11 +94,22 @@ public:
QString typeName() const override;
QString aclAllPattern() const override;
protected:
virtual QString ddlTypeName() const override;
private:
mutable QString createSqlCache;
QString createTableSql() const;
QString generateBodySql(bool isPartition) const;
QString generateInheritsSql() const;
QString partitionBySql() const;
QString partitionKeySql() const;
QString partitionKeyItemSql(
const PartitioningKeyItem &keyItem
) const;
QString getPartitionOfName() const;
QString generateTablespaceSql() const;
QString createViewSql() const;
};

View file

@ -1,8 +1,6 @@
#include "PgClassContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespaceContainer.h"
#include <iterator>
std::string PgClassContainer::getLoadQuery() const
@ -14,17 +12,58 @@ std::string PgClassContainer::getLoadQuery() const
" reloptions, d.description, "
" relacl, pg_get_viewdef(pg_class.oid)";
if (lessThenVersion(120000))
if (lessThenVersion(120000))
q += ", relhasoids ";
if (minimumVersion(100000))
q +=
", pg_get_expr(relpartbound, pg_class.oid)"
", partstrat, partnatts, partattrs, partclass, partcollation "
", pg_get_expr(partexprs, partrelid) AS partexprs";
if (minimumVersion(110000))
q += ", partdefid";
q +=
q +=
"\nFROM pg_catalog.pg_class \n"
" LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n"
" LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n";
if (minimumVersion(100000))
q += " LEFT JOIN pg_partitioned_table AS pt ON (partrelid=pg_class.oid) \n";
q +=
"WHERE relkind IN ('r', 'i', 'p', 'I', 'v', 'm', 'f')";
return q;
}
namespace {
class PartitionedTableKeyItemsBuilder {
public:
int16_t attCount;
std::vector<int16_t> attNums;
std::vector<Oid> attOpClass;
std::vector<Oid> attCollation;
std::vector<QString> expressions;
PartitioningKeyItems Build()
{
int expr_idx = 0;
PartitioningKeyItems result(attCount);
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
auto& item = result[attIdx];
item.attNum = attNums[attIdx];
item.opClass = attOpClass[attIdx];
item.collation = attCollation[attIdx];
if (item.attNum == 0)
item.expression = expressions[expr_idx++];
}
return result;
}
};
}
PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
{
Pgsql::Col col(row);
@ -33,7 +72,7 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
Oid schema_oid = col.nextValue();
PgClass v(m_catalog, class_oid, name, schema_oid);
Oid owner ;
Oid owner;
col >> v.type >> v.oftype
>> owner >> v.am >> v.filenode >> v.tablespace >> v.pages_est
>> v.tuples_est >> v.toastrelid >> v.isshared >> v.persistence
@ -53,5 +92,33 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
if (lessThenVersion(120000))
col >> v.hasoids;
return v;
PgPartitionedTable &pt = v.partitionedTable;
if (minimumVersion(100000))
{
PartitionedTableKeyItemsBuilder kibuilder;
col >> v.partitionBoundaries;
auto strategy = col.nextValue();
if (strategy.null())
{
int s = minimumVersion(110000) ? 5 : 4;
col.skip(s);
}
else
{
pt.strategy << strategy;
col >> kibuilder.attCount;
col.getAsVector<int16_t>(std::back_inserter(kibuilder.attNums));
col.getAsVector<Oid>(std::back_inserter(kibuilder.attOpClass));
col.getAsVector<Oid>(std::back_inserter(kibuilder.attCollation));
col.getAsVector<QString>(std::back_inserter(kibuilder.expressions));
pt.keyColumns = kibuilder.Build();
if (minimumVersion(110000))
col >> pt.defaultPartition;
}
}
return v;
}

View file

@ -1,5 +1,6 @@
#include "PgConstraint.h"
#include "SqlFormattingUtils.h"
#include <QStringBuilder>
void operator<<(ConstraintType &s, const Pgsql::Value &v)
{
@ -176,10 +177,16 @@ QString PgConstraint::typeName() const
QString PgConstraint::dropSql() const
{
return getDropConstraintDefinition(catalog(), *this);
if (isInherited())
return "-- " % objectName() % " is inherited";
return getDropConstraintDefinition(catalog(), *this);
}
QString PgConstraint::createSql() const
{
return getAlterTableConstraintDefinition(catalog(), *this);
if (isInherited())
return "-- " % objectName() % " is inherited";
return getAlterTableConstraintDefinition(catalog(), *this);
}

View file

@ -79,6 +79,10 @@ public:
QString dropSql() const override;
QString createSql() const override;
bool isInherited() const
{
return inhcount > 0;
}
};

View file

@ -4,6 +4,7 @@
#include "Pgsql_declare.h"
#include "Pgsql_Result.h"
#include <QString>
#include <functional>
#include <memory>
#include <vector>
#include <libpq-fe.h>
@ -68,6 +69,15 @@ public:
return nullptr;
}
const T* findIf(std::function<bool(const T&)> func) const
{
auto findResult = std::find_if(m_container.begin(), m_container.end(), func);
if (findResult != m_container.end())
return &*findResult;
return nullptr;
}
const T* getByName(const QString &name) const
{
auto find_res = std::find(m_container.begin(), m_container.end(), name);
@ -78,6 +88,36 @@ public:
return nullptr;
}
const T* getByObjectName(const QString &name) const
{
auto find_res = std::find_if(m_container.begin(), m_container.end(),
[&name](const T& item)
{
return item.objectName() == name;
});
if (find_res != m_container.end())
return &*find_res;
return nullptr;
}
const T* getByObjectNsAndName(const QString &ns, const QString &name) const
{
auto find_res = std::find_if(m_container.begin(), m_container.end(),
[&ns, &name](const T& item)
{
// check name first as it is less likely to pass
return item.objectName() == name
&& item.nsName() == ns;
});
if (find_res != m_container.end())
return &*find_res;
return nullptr;
}
/// Retrieve element by index
///
/// This function throws when idx is out of range

View file

@ -4,17 +4,24 @@
#include "PgContainer.h"
#include "PgInherits.h"
#include "Pgsql_declare.h"
class IFindParents
{
public:
virtual std::vector<Oid> getParentsOf(Oid oid) const = 0;
};
class PgInheritsContainer : public PgContainer<PgInherits, PgInherits::Key> {
class PgInheritsContainer
: public PgContainer<PgInherits, PgInherits::Key>
, public IFindParents
{
public:
using PgContainer<PgInherits, PgInherits::Key>::PgContainer;
virtual std::string getLoadQuery() const override;
/// Returns the parents in the correct order
std::vector<Oid> getParentsOf(Oid oid) const;
std::vector<Oid> getParentsOf(Oid oid) const override;
protected:
PgInherits loadElem(const Pgsql::Row &row) override;
};

View file

@ -5,119 +5,11 @@ namespace {
using KeywordHT = std::unordered_set<Keyword>;
#define PG_KEYWORD(a,b,c) Keyword{a,c},
#define PG_KEYWORD(a,b,c,d) Keyword{a,c},
const KeywordHT _ScanKeywords = {
//const Keyword _ScanKeywords[] = {
#include <server/parser/kwlist.h>
};
#include "kwlist.h"
// The following keyword list are for plpgsql and are copied from the postgresql source.
// src\pl\plpgsql\src\pl_scanner.c
static const KeywordHT plpgsql_reserved_keywords = {
PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD)
PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD)
PG_KEYWORD("by", K_BY, RESERVED_KEYWORD)
PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD)
PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD)
PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD)
PG_KEYWORD("end", K_END, RESERVED_KEYWORD)
PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD)
PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD)
PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD)
PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD)
PG_KEYWORD("if", K_IF, RESERVED_KEYWORD)
PG_KEYWORD("in", K_IN, RESERVED_KEYWORD)
PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD)
PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD)
PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD)
PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD)
PG_KEYWORD("or", K_OR, RESERVED_KEYWORD)
PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD)
PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD)
PG_KEYWORD("to", K_TO, RESERVED_KEYWORD)
PG_KEYWORD("using", K_USING, RESERVED_KEYWORD)
PG_KEYWORD("when", K_WHEN, RESERVED_KEYWORD)
PG_KEYWORD("while", K_WHILE, RESERVED_KEYWORD)
};
static const KeywordHT plpgsql_unreserved_keywords = {
PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
PG_KEYWORD("assert", K_ASSERT, UNRESERVED_KEYWORD)
PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD)
PG_KEYWORD("collate", K_COLLATE, UNRESERVED_KEYWORD)
PG_KEYWORD("column", K_COLUMN, UNRESERVED_KEYWORD)
PG_KEYWORD("column_name", K_COLUMN_NAME, UNRESERVED_KEYWORD)
PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
PG_KEYWORD("constraint", K_CONSTRAINT, UNRESERVED_KEYWORD)
PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME, UNRESERVED_KEYWORD)
PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD)
PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
PG_KEYWORD("datatype", K_DATATYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
PG_KEYWORD("default", K_DEFAULT, UNRESERVED_KEYWORD)
PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD)
PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD)
PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD)
PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
PG_KEYWORD("exception", K_EXCEPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("exit", K_EXIT, UNRESERVED_KEYWORD)
PG_KEYWORD("fetch", K_FETCH, UNRESERVED_KEYWORD)
PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
PG_KEYWORD("get", K_GET, UNRESERVED_KEYWORD)
PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("import", K_IMPORT, UNRESERVED_KEYWORD)
PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD)
PG_KEYWORD("insert", K_INSERT, UNRESERVED_KEYWORD)
PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD)
PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("move", K_MOVE, UNRESERVED_KEYWORD)
PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD)
PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_context", K_PG_CONTEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD)
PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD)
PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS, UNRESERVED_KEYWORD)
PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
PG_KEYWORD("raise", K_RAISE, UNRESERVED_KEYWORD)
PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
PG_KEYWORD("return", K_RETURN, UNRESERVED_KEYWORD)
PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD)
PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("schema", K_SCHEMA, UNRESERVED_KEYWORD)
PG_KEYWORD("schema_name", K_SCHEMA_NAME, UNRESERVED_KEYWORD)
PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD)
PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD)
PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD)
PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD)
PG_KEYWORD("table", K_TABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("table_name", K_TABLE_NAME, UNRESERVED_KEYWORD)
PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD)
PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD)
PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD)
PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD)
PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD)
};
}
@ -131,96 +23,3 @@ const Keyword* getPgsqlKeyword(QString s)
return result;
}
//t_SymbolSet g_Keywords = {
// "a", "abort", "abs", "absent", "absolute", "access", "according", "action", "ada", "add",
// "admin", "after", "aggregate", "all", "allocate", "also", "alter", "analyse", "analyze", "and",
// "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asc", "asensitive",
// "assetion", "assignment", "asymmetric", "at", "atomic", "attribute", "attributes", "authorization", "avg",
// "backward", "base64", "before", "begin", "begin_frame", "begin_partition", "bernoulli", "between", "bigint", "binary",
// "bit", "bit_length", "blob", "blocked", "bom", "boolean", "both", "breadth", "buffer", "by",
// "c", "cache", "call", "called", "cardinality", "cascade", "cascaded", "case", "cast",
// "catalog", "catalog_name", "ceil", "ceiling", "chain", "char", "character", "characteristics",
// "characters", "character_length", "character_set_catalog", "character_set_name", "character_set_schema",
// "char_length", "check", "checkpoint", "class", "class_origin", "clob", "close", "cluster",
// "coalesce", "cobol", "collate", "collation", "collation_catalog", "collation_name", "collation_schema",
// "collect", "column", "columns", "column_name", "command_function", "command_function_code",
// "comment", "comments", "commit", "committed", "concurrently", "condition", "condition_number",
// "configuration", "conflict", "connect", "connection", "connection_name", "constraint", "constraints",
// "constraint_catalog", "constraint_name", "constraint_schema", "constructor", "contains", "content",
// "continue", "control", "conversion", "convert", "copy", "corr", "corresponding", "cost", "count",
// "covar_pop", "covar_samp", "create", "cross", "csv", "cube", "cume_dist", "current", "current_catalog",
// "current_date", "current_default_transform_group", "current_path", "current_role", "current_row",
// "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type",
// "current_user", "cursor", "cursor_name", "cycle",
// "data", "database", "datalink", "date", "datetime_interval_code", "datetime_interval_precision",
// "day", "db", "deallocate", "dec", "decimal", "declare", "default", "defaults", "deferrable", "deferred",
// "defined", "definer", "degree", "delete", "delimiter", "delimiters", "dense_rank", "depends", "depth",
// "deref", "derived", "desc", "describe", "descriptor", "deterministic", "diagnostics", "dictionary",
// "disable", "discard", "disconnect", "dispatch", "distinct", "dlnewcopy", "dlpreviouscopy", "dlurlcomplete",
// "dlurlcompleteonly", "dlurlcompletewrite", "dlurlpatch", "dlurlpathonly", "dlurlpathwrite", "dlurlscheme",
// "dlurlserver", "dlvalue", "do", "document", "domain", "double", "drop", "dynamic", "dynamic_function",
// "dynamic_function_code",
// "each", "element", "else", "empty", "enable", "encodign", "encrypted", "end", "end-exec", "end_frame",
// "end_partition", "enforced", "enum", "equals", "escape", "event", "every", "except", "exception", "exclude",
// "excluding", "exclusive", "exec", "execute", "exists", "exp", "explain", "expression", "extenstion",
// "external", "extract", "false", "family", "fetch", "file", "filter", "final", "first", "first_value",
// "flag", "float", "floor", "following", "for", "force", "foreign", "fortran", "forward", "found",
// "frame_row", "free", "freeze", "from", "fs", "full", "function", "functions", "fusion",
// "g", "general", "generated", "get", "global", "go" "goto", "grant", "granted", "greatest", "group",
// "grouping", "groups", "handler", "having", "header", "hex", "hierarchy", "hold", "hour", "id", "identity",
// "if", "ignore", "ilike", "immediate", "immediatly", "immutable", "implementation", "implicit", "import", "in",
// "including", "increment", "indent", "index", "indexes", "indicator", "inherit", "inherits", "initially", "inline",
// "inner", "inout", "input", "insensitive", "insert", "instance", "instantiable", "instead", "int", "integer",
// "integrity", "intersect", "intersection", "interval", "into", "invoker", "is", "isnull", "isolation",
// "join",
// "k", "key", "key_member", "key_type",
// "label", "lag", "language", "large", "last", "last_value", "lateral", "lead", "leading", "leakproof",
// "least", "left", "length", "level", "library", "like", "like_regex", "limit", "link", "listen", "ln", "load", "local",
// "localtime", "localtimestamp", "location", "locator", "lock", "locked", "logged", "lower",
// "m", "map", "mapping", "match", "matched", "materialized", "max", "maxvalue", "max_cardinality", "member",
// "merge", "message_length", "message_octet_length", "message_text", "method", "min", "minute", "minvalue",
// "mod", "mode", "modifies", "module", "month", "more", "move", "multiset", "mumps",
// "name", "namespace", "national", "natural", "nchar", "nclob", "nesting", "new", "next", "nfc", "nfd", "nfkc", "nkfd",
// "nil", "no", "none", "normalize", "normalize", "not", "nothing", "notify", "notnull", "nowait", "nth_value", "ntile",
// "null", "nullable", "nullif", "nulls", "number", "numeric",
// "object", "occurrences_regex", "octets", "octet_length", "of", "off", "offset", "oids", "old", "on", "only", "open",
// "operator", "option", "options", "or", "order", "ordering", "ordinality", "others", "out", "outer", "output", "over",
// "overlaps", "overlay", "overriding", "owned", "owner",
// "p", "pad", "parallel", "parameter", "parameter_mode", "parameter_name", "parameter_specific_catalog",
// "parameter_specific_name", "parameter_specific_schema", "parser",
// "partial", "partition", "pascal", "passing", "passthrough", "password", "path", "percent", "percentile_cont",
// "percentile_disc", "percent_rank", "period", "permission", "placing", "plans", "pli", "policy", "portion",
// "position", "position_regex", "power", "precedes", "preceding", "precision", "prepare", "prepared", "preserve",
// "primary", "prior", "privileges", "procedural", "procedure", "program", "public",
// "quote", "range", "rank", "read", "reads", "real", "reassign", "recheck", "recovery", "recursive", "ref",
// "references", "referencing", "refresh", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2",
// "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "relative", "release", "rename", "repeatable",
// "replace", "replica", "requiring", "reset", "respect", "restart", "restore", "restrict", "result", "return",
// "returned_cardinality", "returned_length", "returned_octet_length", "returned_sqlstate", "returning", "returns",
// "revoke", "right", "role", "rollback", "rollup", "routine", "routine_catalog", "routine_name", "routine_schema",
// "row", "rows", "row_count", "row_number", "rule",
// "savepoint", "scale", "schema", "schema_name", "scope", "scope_catalog", "scope_name", "scope_schema", "scroll",
// "search", "second", "section", "security", "select", "selective", "self", "sensitive", "sequence", "sequences",
// "serializable", "server", "server_name", "session", "session_user", "set", "setof", "sets", "share", "show",
// "similar", "simple", "size", "skip", "smallint", "snapshot", "some", "source", "space", "specific", "specifictype",
// "specific_name", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "stable",
// "standalone", "start", "state", "statement", "static", "statistics", "stddev_pop", "stddev_samp", "stdin", "stdout",
// "storage", "strict", "strip", "structure", "style", "subclass_origin", "submultiset", "substring", "substring_regex",
// "succeeds", "sum", "symmetric", "sysid", "system", "system_time", "system_user",
// "t", "table", "tables", "tablesample", "tablespace", "table_name", "temp", "template", "temporary", "text", "then",
// "ties", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "token", "top_level_count", "trailing",
// "transaction", "transaction_committed", "transaction_rolled_back", "transaction_active", "transform", "transforms",
// "translate", "translate_regex", "translation", "treat", "trigger", "trigger_catalog", "trigger_name", "trigger_schema",
// "trim", "trim_array", "true", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "under",
// "unencrypted", "union", "unique", "unknown", "unlink", "unlisten", "unlogged", "unnamed", "unnest", "until", "untyped",
// "update", "upper", "uri", "usage", "user", "user_defined_type_catalog", "user_defined_type_code",
// "user_defined_type_name", "user_defined_type_schema", "using",
// "vacuum", "valid", "validate", "validator", "value", "values", "value_of", "varbinary", "varchar", "variadic",
// "varying", "var_pop", "var_samp", "verbose", "version", "versioning", "view", "views", "volatile",
// "when", "whenever", "where", "whitespace", "width_bucket", "window", "with", "within", "without", "work", "wrapper",
// "write", "xml", "xmlagg", "xmlattributes", "xmlbinary", "xmlcast", "xmlcomment", "xmlconcat", "xmldeclaration",
// "xmldocument", "xmlelement", "xmlexists", "xmlforest", "xmliterate", "xmlnamespaces", "xmlparse", "xmlpi",
// "xmlquery", "xmlroot", "xmlschema", "xmlserialize", "xmltable", "xmltext", "xmlvalidate", "year", "yes", "zone"
//};

View file

@ -37,7 +37,7 @@ QString PgObject::fullyQualifiedQuotedObjectName() const
const PgDatabaseCatalog& PgObject::catalog() const
{
return *m_catalog;
return *m_catalog;
}

Some files were not shown because too many files have changed in this diff Show more