Compare commits

..

No commits in common. "main" and "0.1.0" have entirely different histories.
main ... 0.1.0

157 changed files with 1894 additions and 279887 deletions

1
.gitignore vendored
View file

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

View file

@ -5,35 +5,26 @@ variables:
cache:
paths:
- "$PIP_CACHE_DIR"
- "$CI_PROJECT_DIR/pip-cache"
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:
- sphinx-build -b html docs userdocs
artifacts:
paths:
- userdocs
- pip install -U sphinx
- sphinx-build -b html docs public
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:
- tags
- master

18
BUILD Normal file
View file

@ -0,0 +1,18 @@
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

View file

@ -1,13 +0,0 @@
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++20
CONFIG += c++latest
# 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,7 +2,6 @@
#define CSVWRITER_H
#include <ostream>
#include <QString>
#include <QTextStream>
class CsvWriter {

View file

@ -1,5 +1,10 @@
#include "KeyStrengthener.h"
#include <botan/scrypt.h>
#include <botan/base64.h>
#include <QSqlError>
#include <QSqlQuery>
#include <QVariant>
#include <stdexcept>
KeyStrengthener::KeyStrengthener(std::unique_ptr<Botan::PasswordHash> hasher, Botan::secure_vector<uint8_t> salt, size_t keysize)
: m_hasher (std::move(hasher))
@ -31,23 +36,27 @@ Botan::secure_vector<uint8_t> KeyStrengthener::derive(const std::string &passphr
return master_key;
}
void KeyStrengthener::saveParams(SQLiteConnection &db, const QString &table_name)
void KeyStrengthener::saveParams(QSqlDatabase &db, const QString &table_name)
{
size_t i1 = m_hasher->memory_param();
size_t i2 = m_hasher->iterations();
size_t i3 = m_hasher->parallelism();
auto sc = dynamic_cast<Botan::Scrypt*>(m_hasher.get());
size_t i1 = sc->N();
size_t i2 = sc->r();
size_t i3 = sc->p();
auto salt_str = QString::fromUtf8(Botan::base64_encode(m_salt).c_str());
// SAVE parameters in database
auto stmt = db.Prepare("INSERT OR REPLACE INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) "
+ "VALUES(?1, ?2, ?3, ?4, ?5, ?6, ?7)");
stmt.Bind(1, 1);
stmt.Bind(2, "Scrypt");
stmt.Bind(3, (int)i1);
stmt.Bind(4, (int)i2);
stmt.Bind(5, (int)i3);
stmt.Bind(6, (int)m_keySize);
stmt.Bind(7, salt_str);
stmt.Step();
QSqlQuery insert_statement(db);
insert_statement.prepare("INSERT OR REPLACE INTO " + table_name + "(id, algo, i1, i2, i3, ks, salt) "
+ "VALUES(:id, :algo, :i1, :i2, :i3, :ks, :salt)");
insert_statement.bindValue(":id", 1);
insert_statement.bindValue(":algo", "Scrypt");
insert_statement.bindValue(":i1", i1);
insert_statement.bindValue(":i2", i2);
insert_statement.bindValue(":i3", i3);
insert_statement.bindValue(":ks", m_keySize);
insert_statement.bindValue(":salt", salt_str);
if (!insert_statement.exec()) {
throw std::runtime_error("PasswordManager::KeyStrengthener::saveParams failed");
// auto err = insert_statement.lastError();
}
}

View file

@ -1,10 +1,10 @@
#ifndef KEYSTRENGTHENER_H
#define KEYSTRENGTHENER_H
#include <QSqlDatabase>
#include <botan/pwdhash.h>
#include <botan/secmem.h>
#include <memory>
#include "sqlite/SQLiteConnection.h"
class KeyStrengthener {
public:
@ -19,7 +19,7 @@ public:
KeyStrengthener& operator=(KeyStrengthener &&rhs);
Botan::secure_vector<uint8_t> derive(const std::string &passphrase);
void saveParams(SQLiteConnection &db, const QString &table_name);
void saveParams(QSqlDatabase &db, const QString &table_name);
private:
std::unique_ptr<Botan::PasswordHash> m_hasher;
Botan::secure_vector<uint8_t> m_salt;

View file

@ -1,16 +1,33 @@
#include "PasswordManager.h"
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QVariant>
#include <botan/hash.h>
#include <botan/auto_rng.h>
#include <botan/base64.h>
#include <botan/scrypt.h>
#include <botan/nist_keywrap.h>
#include <botan/base64.h>
#include <botan/mac.h>
#include <botan/block_cipher.h>
#include <boost/lexical_cast.hpp>
namespace {
class SqlException : public std::runtime_error {
public:
QSqlError error;
SqlException(const QSqlError &err)
: std::runtime_error(err.text().toUtf8().data())
, error(err)
{}
};
}
using namespace Botan;
class PasswordCryptoEngine {
@ -69,7 +86,7 @@ PasswordManager::PasswordManager() = default;
PasswordManager::~PasswordManager() = default;
bool PasswordManager::initialized(SQLiteConnection& db)
bool PasswordManager::initialized(QSqlDatabase& db)
{
return isPskStoreInitialized(db);
}
@ -85,7 +102,7 @@ PasswordManager::deriveKey(KeyStrengthener &ks, QString passphrase)
return { master_key, mkh };
}
bool PasswordManager::createDatabase(SQLiteConnection &db, QString passphrase)
bool PasswordManager::createDatabase(QSqlDatabase &db, QString passphrase)
{
m_cryptoEngine.reset();
if (!isPskStoreInitialized(db)) {
@ -96,11 +113,15 @@ bool PasswordManager::createDatabase(SQLiteConnection &db, QString passphrase)
auto [master_key, mkh_bin] = deriveKey(ks, passphrase);
auto mkh = QString::fromUtf8(Botan::base64_encode(mkh_bin).c_str());
auto q_ins_hash = db.Prepare(
"INSERT INTO " + m_secretHashTableName + "(id, hash) VALUES(?1, ?2)");
q_ins_hash.Bind(1, 1);
q_ins_hash.Bind(2, mkh);
q_ins_hash.Step();
QSqlQuery q_ins_hash(db);
q_ins_hash.prepare("INSERT INTO " + m_secretHashTableName + "(id, hash) VALUES(:id, :hash)");
q_ins_hash.bindValue(":id", 1);
q_ins_hash.bindValue(":hash", mkh);
if (!q_ins_hash.exec()) {
auto err = q_ins_hash.lastError();
qDebug() << err.text();
throw SqlException(err);
}
m_cryptoEngine = std::make_unique<PasswordCryptoEngine>(master_key);
return true;
@ -108,16 +129,16 @@ bool PasswordManager::createDatabase(SQLiteConnection &db, QString passphrase)
return false;
}
bool PasswordManager::openDatabase(SQLiteConnection &db, QString passphrase)
bool PasswordManager::openDatabase(QSqlDatabase &db, QString passphrase)
{
m_cryptoEngine.reset();
if (isPskStoreInitialized(db)) {
auto ks = getKeyStrengthener(db);
auto [master_key, mkh_bin] = deriveKey(ks, passphrase);
auto q = db.Prepare("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1");
if (q.Step()) {
QByteArray hash_b64 = q.ColumnCharPtr(0);
QSqlQuery q("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1", db);
if (q.next()) {
auto hash_b64 = q.value(0).toString().toUtf8();
auto hash_bin = Botan::base64_decode(hash_b64.data(), static_cast<size_t>(hash_b64.size()));
if (hash_bin == mkh_bin) {
m_cryptoEngine = std::make_unique<PasswordCryptoEngine>(master_key);
@ -135,19 +156,7 @@ void PasswordManager::closeDatabase()
bool PasswordManager::locked() const
{
return m_cryptoEngine == nullptr;
}
void PasswordManager::resetMasterPassword(SQLiteConnection &db)
{
if (!isPskStoreInitialized(db))
return;
closeDatabase();
auto del_algo = db.Prepare("DELETE FROM " + m_secretAlgoTableName + " WHERE id=1");
del_algo.Step();
auto del_hash = db.Prepare("DELETE FROM " + m_secretHashTableName + " WHERE id=1");
del_hash.Step();
return m_cryptoEngine == nullptr;
}
std::string PasswordManager::encrypt(const std::string &name, const std::string &passwd)
@ -179,13 +188,14 @@ std::string PasswordManager::decrypt(const std::string &id, const std::string_vi
}
}
void PasswordManager::initializeNewPskStore(SQLiteConnection &db)
void PasswordManager::initializeNewPskStore(QSqlDatabase &db)
{
// // Create tables
// // - psk_masterkey_algo
// // - psk_passwd
{
auto create_tbl = db.Prepare(
QSqlQuery create_tbl(db);
create_tbl.prepare(
"CREATE TABLE IF NOT EXISTS " + m_secretAlgoTableName + "( \n"
" id INTEGER PRIMARY KEY, \n"
" algo TEXT, \n"
@ -195,55 +205,70 @@ void PasswordManager::initializeNewPskStore(SQLiteConnection &db)
" ks INTEGER, \n"
" salt TEXT \n"
");");
create_tbl.Step();
if (!create_tbl.exec()) {
auto err = create_tbl.lastError();
throw SqlException(err);
}
}
auto create_tbl = db.Prepare(
QSqlQuery create_tbl(db);
create_tbl.prepare(
"CREATE TABLE IF NOT EXISTS " + m_secretHashTableName + "( \n"
" id INTEGER PRIMARY KEY, \n"
" hash TEXT \n"
");");
create_tbl.Step();
if (!create_tbl.exec()) {
auto err = create_tbl.lastError();
throw SqlException(err);
}
}
bool PasswordManager::isPskStoreInitialized(SQLiteConnection& db)
bool PasswordManager::isPskStoreInitialized(QSqlDatabase& db)
{
// Is the table with the secret data present and filled?
auto query = db.Prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?1");
query.Bind(1, m_secretAlgoTableName);
if (!query.Step()) {
QSqlQuery query(db);
query.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=:name");
query.bindValue(":name", m_secretAlgoTableName);
if (!query.exec()) {
auto err = query.lastError();
throw SqlException(err);
}
if (!query.next()) {
return false;
}
query.Reset();
query.Bind(1, m_secretHashTableName);
if (!query.Step()) {
query.bindValue(":name", m_secretHashTableName);
if (!query.exec()) {
auto err = query.lastError();
throw SqlException(err);
}
if (!query.next()) {
return false;
}
auto sel_algo = db.Prepare("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1");
if (!sel_algo.Step()) {
QSqlQuery sel_algo("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1", db);
if (!sel_algo.next()) {
return false;
}
auto sel_hash = db.Prepare("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1");
if (!sel_hash.Step()) {
QSqlQuery sel_hash("SELECT hash FROM " + m_secretHashTableName + " WHERE id=1", db);
if (!sel_hash.next()) {
return false;
}
return true;
}
KeyStrengthener PasswordManager::getKeyStrengthener(SQLiteConnection &db)
KeyStrengthener PasswordManager::getKeyStrengthener(QSqlDatabase &db)
{
auto query = db.Prepare("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1");
if (query.Step()) {
std::string algo = query.ColumnCharPtr(0);
size_t i1 = query.ColumnInteger(1);
size_t i2 = query.ColumnInteger(2);
size_t i3 = query.ColumnInteger(3);
size_t ks = query.ColumnInteger(4);
QByteArray salt = query.ColumnCharPtr(5);
QSqlQuery query("SELECT algo, i1, i2, i3, ks, salt FROM " + m_secretAlgoTableName + " WHERE id=1", db);
if (query.next()) {
std::string algo = query.value(0).toString().toUtf8().data();
size_t i1 = query.value(1).toUInt();
size_t i2 = query.value(2).toUInt();
size_t i3 = query.value(3).toUInt();
size_t ks = query.value(4).toUInt();
auto salt = query.value(5).toString().toUtf8();
auto pwh_fam = Botan::PasswordHashFamily::create(algo);
return KeyStrengthener(
@ -272,4 +297,3 @@ KeyStrengthener PasswordManager::createKeyStrengthener()
key_size
);
}

78
core/PasswordManager.h Normal file
View file

@ -0,0 +1,78 @@
#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,13 +255,6 @@ 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,6 +5,7 @@
#-------------------------------------------------
QT -= gui
QT += sql
TARGET = core
TEMPLATE = lib
@ -20,13 +21,16 @@ 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 += \
HEADERS += PasswordManager.h \
KeyStrengthener.h \
SqlLexer.h \
ScopeGuard.h \
CsvWriter.h \

View file

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

View file

@ -18,11 +18,11 @@
# -- Project information -----------------------------------------------------
project = 'pgLab User Manual'
copyright = '2021-2022, Eelke Klein'
copyright = '2021, Eelke Klein'
author = 'Eelke Klein'
# The full version, including alpha/beta/rc tags
# release = '1.0'
release = 'https://eelke.gitlab.io/pgLab/'
# -- General configuration ---------------------------------------------------
@ -31,7 +31,6 @@ author = 'Eelke Klein'
# 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.
@ -48,13 +47,9 @@ 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 = 'furo'
html_theme = 'alabaster'
# 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 = ['assets']
html_css_files = [
'custom.css'
]
html_static_path = ['_static']

View file

@ -1,35 +0,0 @@
===================
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

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

View file

@ -1,34 +0,0 @@
================
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

@ -1,100 +0,0 @@
###############
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,22 +4,19 @@
contain the root `toctree` directive.
pgLab User Manual
=================
====================================================
.. toctree::
:maxdepth: 3
:maxdepth: 2
: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/

View file

@ -1,42 +0,0 @@
============
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.

View file

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

View file

@ -1,26 +0,0 @@
#######
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.

View file

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

View file

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

View file

@ -1,31 +0,0 @@
#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/"));
}

View file

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

View file

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

View file

@ -102,7 +102,6 @@ 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);
@ -120,7 +119,6 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(
cmbbxSsl->setModelColumn(0);
auto ssl_model = new SslModeModel(this);
cmbbxSsl->setModel(ssl_model);
cmbbxSsl->setEditable(true);
lblSsl->setBuddy(cmbbxSsl);
lblCert = new QLabel;
@ -207,21 +205,13 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
edtPassword->setText("");
cmbDbname->setCurrentText(cfg.dbname());
cmbbxSsl->setCurrentText(cfg.getParameter("sslmode"));
cmbbxSsl->setCurrentIndex(static_cast<int>(cfg.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
@ -234,7 +224,7 @@ ConnectionConfig ConnectionConfigurationWidget::data() const
cfg.setUser(edtUser->text());
cfg.setPassword(edtPassword->text());
cfg.setDbname(cmbDbname->currentText());
cfg.setParameter("sslmode", cmbbxSsl->currentText());
cfg.setSslMode(static_cast<SslMode>(cmbbxSsl->currentIndex()));
cfg.setSslCert(edtCert->text());
cfg.setSslKey(edtKey->text());
cfg.setSslRootCert(edtRootCert->text());
@ -248,14 +238,9 @@ QString ConnectionConfigurationWidget::group() const
return cmbbxGroup->currentText();
}
bool ConnectionConfigurationWidget::savePasswordEnabled() const
bool ConnectionConfigurationWidget::savePassword() const
{
return cbSavePassword->isChecked();
}
bool ConnectionConfigurationWidget::passwordIsChanged() const
{
return passwordChanged;
return cbSavePassword->isChecked() && !edtPassword->text().isEmpty();
}
void ConnectionConfigurationWidget::testConnection()
@ -293,8 +278,3 @@ void ConnectionConfigurationWidget::handleTestResult(TestConnectionResult result
cmbDbname->setCurrentText(current);
}
void ConnectionConfigurationWidget::passwordEdited(const QString &)
{
passwordChanged = true;
}

View file

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

View file

@ -2,12 +2,11 @@
#include "MasterController.h"
#include "ConnectionManagerWindow.h"
#include "ConnectionListModel.h"
#include "utils/PasswordManager.h"
#include "PasswordManager.h"
#include "DatabaseWindow.h"
#include "BackupDialog.h"
#include "PasswordPromptDialog.h"
#include "ConnectionConfigurationWidget.h"
#include <QSqlQuery>
#include <QInputDialog>
#include <QMessageBox>
#include <QTimer>
@ -39,6 +38,7 @@ void ConnectionController::init()
m_connectionTreeModel->load();
m_connectionManagerWindow = new ConnectionManagerWindow(m_masterController, nullptr);
m_connectionManagerWindow->show();
}
void ConnectionController::showConnectionManager()
@ -53,21 +53,16 @@ void ConnectionController::openSqlWindowForConnection(QModelIndex index)
if (retrieveConnectionPassword(*config)) {
m_connectionTreeModel->save(*config);
openSqlWindowForConnection(*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::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)
@ -84,14 +79,10 @@ void ConnectionController::openBackupDlgForConnection(QModelIndex index)
}
}
void ConnectionController::createConnection(ConnectionConfig *init)
void ConnectionController::createConnection()
{
ConnectionConfig cc;
if (init)
{
cc = *init;
cc.setUuid(QUuid());
}
cc.setUuid(QUuid::createUuid());
editConfig(cc);
}
@ -108,7 +99,7 @@ void ConnectionController::editCopy(QModelIndex index)
auto config = ConnectionTreeModel::getConfigFromModelIndex(index);
if (config) {
auto cc = *config;
cc.setUuid(QUuid());
cc.setUuid(QUuid::createUuid());
cc.setEncodedPassword({}); // maybe we should decode en reencode?
editConfig(cc);
}
@ -124,19 +115,8 @@ void ConnectionController::saveConnection(ConnectionConfigurationWidget &w)
{
auto cc = w.data();
auto grp = w.group();
bool isNew = cc.uuid().isNull();
if (isNew)
{
cc.setUuid(QUuid::createUuid());
}
if (w.savePasswordEnabled())
{
if (isNew || w.passwordIsChanged())
encryptPassword(cc);
}
else
{
cc.setEncodedPassword({});
if (w.savePassword()) {
encryptPassword(cc);
}
m_connectionTreeModel->save(grp, cc);
}
@ -146,14 +126,11 @@ void ConnectionController::addGroup()
auto result = QInputDialog::getText(nullptr, tr("Add new connection group"),
tr("Group name"));
if (!result.isEmpty()) {
try
{
m_connectionTreeModel->addGroup(result);
}
catch (const SQLiteException &ex) {
auto res = m_connectionTreeModel->addGroup(result);
if (std::holds_alternative<QSqlError>(res)) {
QMessageBox::critical(nullptr, tr("Add group failed"),
tr("Failed to add group.\n") +
QString(ex.what()));
std::get<QSqlError>(res).text());
}
}
}
@ -179,36 +156,33 @@ 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)
@ -221,7 +195,7 @@ bool ConnectionController::retrieveFromPasswordManager(const std::string &passwo
password = m_passwordManager->decrypt(password_id, enc_password);
return true;
}
catch (const PasswordManagerException &)
catch (const PasswordManagerException &ex)
{
return false;
}
@ -250,43 +224,29 @@ 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)
{
while (true) {
// ask user for passphrase
PassphraseResult pp_result = PassphrasePrompt();
if (!pp_result.success)
break;
break; // leave this retry loop
// 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;
}
@ -337,7 +297,7 @@ bool ConnectionController::InitializePasswordManager()
std::string ConnectionController::getPskId(QUuid connectionid)
{
std::string id = "dbpw/";
id += connectionid.toString(QUuid::WithBraces).toUtf8().data();
id += connectionid.toString().toUtf8().data();
return id;
}

View file

@ -12,7 +12,6 @@ class ConnectionTreeModel;
class ConnectionManagerWindow;
class PasswordManager;
class QTimer;
class QSqlDatabase;
class PassphraseResult {
@ -38,12 +37,11 @@ public:
void showConnectionManager();
void openSqlWindowForConnection(QModelIndex index);
void openSqlWindowForConnection(const ConnectionConfig &cfg);
void openBackupDlgForConnection(QModelIndex index);
void openBackupDlgForConnection(QModelIndex index);
/// Starts the form for creating a new conncetion.
/// This function returns immidiatly!
void createConnection(ConnectionConfig *init = nullptr);
void createConnection();
/// Starts the form for editing a conncetion.
/// This function returns immidiatly!
void editConnection(QModelIndex index);
@ -60,7 +58,6 @@ public:
bool UnlockPasswordManagerIfNeeded();
bool decodeConnectionPassword(QUuid id, QByteArray encoded, QString &out_password);
void resetPasswordManager();
private:
MasterController *m_masterController;
ConnectionList *m_connectionList = nullptr;
@ -74,6 +71,7 @@ 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,20 +1,16 @@
#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 {
@ -45,195 +41,59 @@ 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;)__";
// 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);
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);
)__" ;
QByteArray b64; // needs to stay in scope until query is executed
SQLiteTransaction tx(db);
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, {}};
}
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();
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());
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();
if (!q.exec()) {
auto sql_error = q.lastError();
return { sql_error };
}
return {};
}
} // end of unnamed namespace
ConnectionTreeModel::ConnectionTreeModel(QObject *parent, SQLiteConnection &db)
ConnectionTreeModel::ConnectionTreeModel(QObject *parent, QSqlDatabase &db)
: QAbstractItemModel(parent)
, m_db(db)
{
@ -241,66 +101,60 @@ ConnectionTreeModel::ConnectionTreeModel(QObject *parent, SQLiteConnection &db)
void ConnectionTreeModel::load()
{
//InitConnectionTables(m_db);
MigrationDirector md(m_db);
md.Execute();
InitConnectionTables(m_db);
loadGroups();
loadConnections();
}
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();
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);
}
}
auto g = std::make_shared<ConnectionGroup>();
g->conngroup_id = id;
g->name = name;
m_groups.push_back(g);
}
void ConnectionTreeModel::loadConnections()
{
auto stmt = m_db.Prepare(
"SELECT uuid, cname, conngroup_id, password "
"FROM connection ORDER BY conngroup_id, cname;");
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());
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)
);
}
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");
}
}
}
QVariant ConnectionTreeModel::data(const QModelIndex &index, int role) const
@ -422,7 +276,15 @@ 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();
RemoveConnection(m_db, 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");
}
}
beginRemoveRows(parent, row, row + count - 1);
SCOPE_EXIT { endRemoveRows(); };
@ -442,7 +304,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);
emit dataChanged(
dataChanged(
createIndex(conn_idx, 0, node.get()),
createIndex(conn_idx, ColCount-1, node.get()));
saveToDb(*node);
@ -460,8 +322,14 @@ 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
new_grp_idx = addGroup(group_name);
// 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");
}
}
auto new_grp = m_groups[new_grp_idx];
@ -470,23 +338,20 @@ 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);
saveToDb(*node);
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());
}
}
void ConnectionTreeModel::save(const ConnectionConfig &cc)
{
saveToDb(cc);
}
void ConnectionTreeModel::clearAllPasswords()
{
for (auto group : m_groups)
for (auto cc : group->connections())
{
cc->setEncodedPassword({});
saveToDb(*cc);
}
saveToDb(cc);
}
std::tuple<int, int> ConnectionTreeModel::findConfig(const QUuid uuid) const
@ -516,13 +381,17 @@ int ConnectionTreeModel::findGroup(const QString &name) const
return -1;
}
int ConnectionTreeModel::addGroup(const QString &group_name)
std::variant<int, QSqlError> ConnectionTreeModel::addGroup(const QString &group_name)
{
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();
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();
cg->name = group_name;
int row = m_groups.size();
@ -532,21 +401,27 @@ int ConnectionTreeModel::addGroup(const QString &group_name)
return row;
}
void ConnectionTreeModel::removeGroup(int row)
std::optional<QSqlError> ConnectionTreeModel::removeGroup(int row)
{
beginRemoveRows({}, row, row);
SCOPE_EXIT { endRemoveRows(); };
auto id = m_groups[row]->conngroup_id;
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();
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 };
}
m_groups.remove(row);
return {};
}
int ConnectionTreeModel::findGroup(int conngroup_id) const
@ -574,9 +449,9 @@ ConnectionGroup *ConnectionTreeModel::getGroupFromModelIndex(QModelIndex index)
return dynamic_cast<ConnectionGroup*>(node);
}
void ConnectionTreeModel::saveToDb(const ConnectionConfig &cc)
std::optional<QSqlError> ConnectionTreeModel::saveToDb(const ConnectionConfig &cc)
{
SaveConnectionConfig(m_db, cc, cc.parent()->conngroup_id);
return SaveConnectionConfig(m_db, cc, cc.parent()->conngroup_id);
}

View file

@ -11,7 +11,8 @@
#include <variant>
#include <QVector>
#include "sqlite/SQLiteConnection.h"
#include <QSqlError>
class QSqlDatabase;
class ConnectionTreeModel : public QAbstractItemModel {
Q_OBJECT
@ -26,7 +27,7 @@ public:
ColCount
};
ConnectionTreeModel(QObject *parent, SQLiteConnection &db);
ConnectionTreeModel(QObject *parent, QSqlDatabase &db);
void load();
@ -58,10 +59,9 @@ 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
int addGroup(const QString &group_name);
void removeGroup(int row);
std::variant<int, QSqlError> addGroup(const QString &group_name);
std::optional<QSqlError> 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>>;
SQLiteConnection &m_db;
QSqlDatabase &m_db;
Groups m_groups;
/// Finds the connection with the specified uuid and returns
@ -78,11 +78,8 @@ private:
std::tuple<int, int> findConfig(const QUuid uuid) const;
int findGroup(const QString &name) const;
void saveToDb(const ConnectionConfig &cc);
std::optional<QSqlError> 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,27 +102,3 @@ 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,12 +39,6 @@ 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,25 +26,16 @@
<x>0</x>
<y>0</y>
<width>413</width>
<height>29</height>
<height>30</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">
@ -173,26 +164,6 @@ 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 class DataMeaning {
Normal,
Bytes ///< the value represents bytes pretty print in KiB, MiB, GiB, TiB, PiB, EiB
enum DataMeaning {
DataMeaningNormal,
DataMeaningBytes ///< the value represents bytes pretty print in KiB, MiB, GiB, TiB, PiB, EiB
};
enum CustomDataRole {

View file

@ -1,5 +1,4 @@
#include "DatabaseWindow.h"
#include "About.h"
#include "ui_DatabaseWindow.h"
#include "util.h"
#include "crud/CrudTab.h"
@ -105,7 +104,7 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config)
try
{
QString title = "pglab - ";
title += m_config.makeLongDescription();
title += m_config.name();
setWindowTitle(title);
auto cfg = m_config;
@ -306,7 +305,19 @@ void DatabaseWindow::dropEvent(QDropEvent *event)
void DatabaseWindow::on_actionAbout_triggered()
{
ShowAboutDialog(this);
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."
));
}
@ -442,15 +453,3 @@ 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,9 +105,6 @@ 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>21</height>
<height>29</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -44,7 +44,6 @@
<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"/>
@ -95,7 +94,6 @@
<property name="title">
<string>Help</string>
</property>
<addaction name="actionManual"/>
<addaction name="actionAbout"/>
</widget>
<addaction name="menuFile"/>
@ -300,17 +298,11 @@
<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">
@ -327,16 +319,6 @@
<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,8 +1,5 @@
#include "MasterController.h"
#include "ConnectionController.h"
#include "utils/PostgresqlUrlParser.h"
#include <ConnectionConfig.h>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QStandardPaths>
@ -30,23 +27,18 @@ MasterController::~MasterController()
void MasterController::init()
{
m_userConfigDatabase.Open(GetUserConfigDatabaseName());
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_connectionController = new ConnectionController(this);
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();
m_connectionController->init();
}
ConnectionController *MasterController::connectionController()
@ -54,7 +46,7 @@ ConnectionController *MasterController::connectionController()
return m_connectionController;
}
SQLiteConnection& MasterController::userConfigDatabase()
QSqlDatabase& 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();
SQLiteConnection& userConfigDatabase();
QSqlDatabase& userConfigDatabase();
signals:
public slots:
private:
SQLiteConnection m_userConfigDatabase;
QSqlDatabase m_userConfigDatabase;
ConnectionController* m_connectionController = nullptr;
};

View file

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

View file

@ -1,4 +1,5 @@
#include "ResultTableModelUtil.h"
#include "Pgsql_oids.h"
#include <QTableView>
#include <QHeaderView>
@ -27,7 +28,29 @@ 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,36 +1,10 @@
#pragma once
#include "Pgsql_oids.h"
#include "Pgsql_declare.h"
#include <QAbstractTableModel>
#include <QColor>
#include <util/Colors.h>
Qt::Alignment GetDefaultAlignmentForType(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;
// }
QColor GetDefaultColorForType(Oid oid);
inline Qt::Alignment GetDefaultAlignment() { return Qt::AlignLeft | Qt::AlignVCenter; }
inline Qt::Alignment GetDefaultBoolAlignment() { return Qt::AlignCenter | Qt::AlignVCenter; }
@ -38,14 +12,13 @@ inline Qt::Alignment GetDefaultNumberAlignment() { return Qt::AlignRight | Qt::A
inline QColor GetDefaultBoolColor(bool v)
{
const ColorTheme& colorTheme = GetColorTheme();
return v ? colorTheme.booleanTrue : colorTheme.booleanFalse;
return v ? Qt::darkGreen : Qt::darkRed;
}
// 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>(DataMeaning::Normal);
return static_cast<int>(DataMeaningNormal);
}

View file

@ -12,7 +12,6 @@
#include "ScopeGuard.h"
#include "SqlFormattingUtils.h"
#include <QBrush>
#include <QStringBuilder>
namespace {
@ -30,13 +29,6 @@ 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) {
@ -150,6 +142,18 @@ 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;
}
@ -187,7 +191,7 @@ QVariant ColumnTableModel::getData(const QModelIndex &index) const
v = QString::fromStdU16String(t.notnull ? u"" : u"N");
break;
case DefaultCol:
v = getDefaultString(t);
v = t.defaultValue;
break;
case ForeignKeyCol:
v = getFKey(t);
@ -218,36 +222,7 @@ QString ColumnTableModel::getFKey(const PgAttribute &column) const
result = getForeignKeyConstraintReferencesShort(*m_catalog, elem);
}
}
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 {};
return result;
}
void ColumnTableModel::refresh()
@ -292,19 +267,22 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
QVariant v;
const auto &t = m_columns[index.row()];
if (t.typid == InvalidOid)
v = QBrush(theme.defaultTextColor);
v = QBrush(Qt::black);
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(theme.numericTypeColor);
v = QBrush(Qt::darkBlue);
break;
case TypCategory::DateTime:
case TypCategory::Timespan:
v = QBrush(theme.dateTimeTypeColor);
v = QBrush(Qt::darkMagenta);
break;
case TypCategory::String:
v = QBrush(theme.stringTypeColor);
v = QBrush(Qt::darkYellow);
break;
case TypCategory::Array:
case TypCategory::Composite:
@ -317,8 +295,7 @@ QVariant ColumnTableModel::data(const QModelIndex &index, int role) const
case TypCategory::BitString:
case TypCategory::Unknown:
default:
v = QBrush(theme.defaultTextColor);
break;
v = QBrush(Qt::black);
}
}
return v;

View file

@ -1,7 +1,6 @@
#ifndef COLUMNTABLEMODEL_H
#define COLUMNTABLEMODEL_H
#include "util/Colors.h"
#include "catalog/models/BaseTableModel.h"
#include "catalog/PgAttribute.h"
#include "catalog/PgDatabaseCatalog.h"
@ -30,8 +29,7 @@ public:
colCount };
explicit ColumnTableModel(QObject *parent = nullptr);
using BaseTableModel::BaseTableModel;
void setData(std::shared_ptr<const PgDatabaseCatalog> cat, const std::optional<PgClass> &table);
// Header:
@ -57,10 +55,8 @@ 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,9 +55,6 @@ 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;
@ -88,17 +85,11 @@ int ConstraintModel::columnCount(const QModelIndex &) const
// return v;
//}
Oid ConstraintModel::getType(int col) const
Oid ConstraintModel::getType(int ) const
{
Oid oid;
switch (col) {
case InheritedCol:
oid = Pgsql::bool_oid;
break;
default:
oid = Pgsql::varchar_oid;
}
return oid;
Oid oid = Pgsql::varchar_oid;
return oid;
}
QString IconForConstraintType(ConstraintType ct)
@ -137,27 +128,25 @@ QVariant ConstraintModel::getData(const QModelIndex &index) const
const auto &t = m_constraints[row];
const int col = index.column();
switch (col) {
QString s;
switch (col) {
case TypeCol:
v = IconForConstraintType(t.type);
s = IconForConstraintType(t.type);
break;
case NameCol:
v = t.objectName();
s = t.objectName();
break;
case NsCol:
v = t.nsName();
s = t.nsName();
break;
case SupportingIndexCol:
v = getIndexDisplayString(*m_catalog, t.indid);
s = 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,7 +19,6 @@ public:
NameCol, ///
NsCol, ///
SupportingIndexCol,
InheritedCol,
// DefinitionCol,
colCount };

View file

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

View file

@ -5,16 +5,15 @@
#include "catalog/PgClass.h"
#include "catalog/PgClassContainer.h"
#include "catalog/PgNamespace.h"
#include "catalog/PgInheritsContainer.h"
//#include "Pgsql_declare.h"
#include "ui/catalog/tables/TableTreeBuilder.h"
#include "catalog/PgNamespaceContainer.h"
#include "Pgsql_declare.h"
#include "CustomDataRole.h"
#include <QBrush>
#include <QtConcurrent>
#include "Pgsql_Connection.h"
TablesTableModel::TablesTableModel(std::shared_ptr<OpenDatabase> opendatabase, QObject *parent)
: QAbstractItemModel(parent)
: QAbstractTableModel(parent)
, openDatabase(opendatabase)
{}
@ -34,21 +33,6 @@ 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();
@ -57,19 +41,32 @@ 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));
}
@ -96,46 +93,14 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
int TablesTableModel::rowCount(const QModelIndex &parent) const
int TablesTableModel::rowCount(const QModelIndex &) const
{
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;
return static_cast<int>(m_tables.size());
}
int TablesTableModel::columnCount(const QModelIndex &) const
{
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 {};
return colCount;
}
Oid TablesTableModel::getType(int column) const
@ -164,29 +129,37 @@ Oid TablesTableModel::getType(int column) const
QVariant TablesTableModel::getData(const QModelIndex &index) const
{
// const auto &table = rootNode->children[index.row()];
const TableNode* table = nodeFromIndex(index);
const auto &t = table->_class;
const auto &s = table->sizes;
const auto &table = m_tables[index.row()];
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)
@ -200,16 +173,16 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const
case TableSizeCol:
case IndexSizeCol:
case ToastSizeCol:
return static_cast<int>(DataMeaning::Bytes);
return static_cast<int>(DataMeaningBytes);
default:
return static_cast<int>(DataMeaning::Normal);
return static_cast<int>(DataMeaningNormal);
}
}
return QVariant();
}
void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
void TablesTableModel::StartLoadTableSizes(std::map<Oid, int> oidIndex)
{
QPointer p(this);
QtConcurrent::run([this]
@ -219,7 +192,7 @@ void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
.then(qApp, [p, oidIndex] (TableSizes sizes)
{
if (p)
p.data()->PopulateSizes(oidIndex, sizes);
p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes));
});
}
@ -268,41 +241,18 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const
return sizes;
}
void TablesTableModel::PopulateSizes(
const OidClassIndex &oidIndex,
const std::vector<TableSize> &sizes
)
void TablesTableModel::PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes)
{
for (auto s : sizes)
{
auto findIter = oidIndex.find(s.oid);
if (findIter != oidIndex.end())
{
findIter->second->sizes = s;
m_tables[findIter->second].sizes = s;
}
}
emit dataChanged(
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");
createIndex(0, TotalSizeCol),
createIndex(static_cast<int>(m_tables.size()), ToastSizeCol)
);
}

View file

@ -1,12 +1,9 @@
#ifndef TABLESTABLEMODEL_H
#ifndef TABLESTABLEMODEL_H
#define TABLESTABLEMODEL_H
//#include "catalog/models/BaseTableModel.h"
#include "ui/catalog/tables/TableNode.h"
#include "ui/catalog/tables/TableSize.h"
#include "BaseTableModel.h"
#include "NamespaceFilter.h"
#include "catalog/PgClass.h"
#include <QAbstractItemModel>
#include <memory>
#include <vector>
@ -14,7 +11,7 @@ class OpenDatabase;
class PgClass;
class PgDatabaseCatalog;
class TablesTableModel: public QAbstractItemModel {
class TablesTableModel: public QAbstractTableModel {
public:
using RowItem = PgClass;
enum e_Columns : int {
@ -43,31 +40,42 @@ public:
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
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
virtual QVariant data(const QModelIndex &index, int role) const override;
PgClass getTable(int row) const;
RowItem rowItem(int row) const
{
return nodeFromIndex(index)->_class;
return getTable(row);
}
static const TableNode* nodeFromIndex(const QModelIndex &index);
Oid getTableOid(int row) const;
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>;
// 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
class Table {
public:
PgClass _class;
TableSize sizes;
Table(const PgClass &cls)
: _class(cls)
{}
};
using t_Tables = std::vector<Table>;
std::shared_ptr<const PgDatabaseCatalog> m_catalog;
NamespaceFilter m_namespaceFilter = NamespaceFilter::User;
std::shared_ptr<TableNode> rootNode;
t_Tables m_tables;
QMetaObject::Connection refreshConnection;
std::shared_ptr<OpenDatabase> openDatabase;
@ -75,17 +83,10 @@ private:
Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const;
using OidClassIndex = std::map<Oid, std::shared_ptr<TableNode>>;
void StartLoadTableSizes(OidClassIndex oidIndex);
void StartLoadTableSizes(std::map<Oid, int> oidIndex);
TableSizes QueryTableSizes() const;
void PopulateSizes(
const OidClassIndex &oidIndex,
const std::vector<TableSize> &sizes
);
void PopulateSizes(std::map<Oid, int> oidIndex, std::vector<TableSize> sizes);
std::function<bool(const PgClass&)> GetNamespaceFilterLambda();
private slots:
void refresh();

View file

@ -34,21 +34,13 @@ void CatalogConstraintPage::setFilter(const std::optional<PgClass> &cls)
void CatalogConstraintPage::tableView_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{
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;
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);
}

View file

@ -22,7 +22,6 @@ 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/tables/TablesTableModel.h"
#include "catalog/models/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.itemView();
auto tv = m_tablesTableView.tableView();
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 tableNode = TablesTableModel::nodeFromIndex(m_tablesTableView.sortFilter()->mapToSource(current)); // m_tablesTableView.rowItemForProxyIndex(current);
selectedTableChanged(tableNode->_class);
auto table = m_tablesTableView.rowItemForProxyIndex(current);
selectedTableChanged(table);
}
@ -127,15 +127,11 @@ void CatalogTablesPage::tableListTable_layoutChanged(const QList<QPersistentMode
void CatalogTablesPage::on_tableListTable_doubleClicked(const QModelIndex &index)
{
auto sourceIndex = m_tablesTableView.sortFilter()->mapToSource(index);
auto tableNode = TablesTableModel::nodeFromIndex(sourceIndex);
if (tableNode)
{
Oid oid = tableNode->_class.oid();
if (oid != InvalidOid) {
tableSelected(oid);
}
}
auto row = m_tablesTableView.sortFilter()->mapToSource(index).row();
PgClass table = m_tablesTableView.dataModel()->getTable(row);
if (table.oid() != InvalidOid) {
tableSelected(table.oid());
}
}
void CatalogTablesPage::selectedTableChanged(const std::optional<PgClass> &table)

View file

@ -2,13 +2,13 @@
#define CATALOGTABLESPAGE_H
#include "NamespaceFilter.h"
#include "catalog/tables/TablesTableModel.h"
#include "catalog/models/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, QTreeView> m_tablesTableView;
PgLabTableViewHelper<TablesTableModel> m_tablesTableView;
// Details
QTabWidget *m_detailsTabs = nullptr;

View file

@ -14,7 +14,6 @@
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)));
@ -70,7 +69,8 @@ void CodeEditor::resizeEvent(QResizeEvent *e)
void CodeEditor::highlightCurrentLine()
{
QTextEdit::ExtraSelection selection;
selection.format.setBackground(colorTheme.currentLine);
QColor lineColor = QColor(Qt::yellow).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
selection.cursor = textCursor();
selection.cursor.clearSelection();
@ -87,7 +87,8 @@ void CodeEditor::onTextChanged()
void CodeEditor::addErrorMarker(int position, int length)
{
QTextEdit::ExtraSelection selection;
selection.format.setBackground(colorTheme.errorLine);
QColor lineColor = QColor(Qt::red).lighter(160);
selection.format.setBackground(lineColor);
selection.format.setFontItalic(true);
selection.cursor = textCursor();
selection.cursor.setPosition(position);

View file

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

View file

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

View file

@ -1,6 +1,5 @@
#include <QPainter>
#include <QTextBlock>
#include "util/Colors.h"
#pragma once
@ -14,13 +13,10 @@ 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() + 1;
return index.row();
else if (role == CustomDataTypeRole)
return Pgsql::int4_oid;
@ -231,10 +231,9 @@ void CrudModel::loadIntoModel(std::shared_ptr<Pgsql::Result> data)
beginResetModel();
m_pendingRowList.clear();
m_roData = data;
initializeColumnList();
lastRowKey = data->rows() - 1;
initRowMapping();
appendNewRowInternal();
appendNewRow();
endResetModel();
}
@ -247,9 +246,9 @@ void CrudModel::initRowMapping()
m_rowMapping.emplace_back(i);
}
void CrudModel::connectionStateChanged()
void CrudModel::connectionStateChanged(ASyncDBConnection::StateData state)
{
switch (m_dbConn.state()) {
switch (state.State) {
case ASyncDBConnection::State::NotConnected:
break;
case ASyncDBConnection::State::Connecting:
@ -277,7 +276,7 @@ Qt::ItemFlags CrudModel::flags(const QModelIndex &index) const
if (index.column() < PreColumnCount)
return flags;
if (m_primaryKey && !columnIsReadOnly(index.column()))
if (m_primaryKey)
flags |= Qt::ItemIsEditable;
return flags;
@ -420,16 +419,21 @@ 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;
auto&& column = columnList[num];
if (first)
first = false;
else
q << ",";
q << quoteIdent(column.name);
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;
}
}
q << ") VALUES ($1";
for (size_t p = 2; p <= data.size(); ++p)
@ -511,30 +515,6 @@ 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();
@ -547,15 +527,10 @@ void CrudModel::revert()
void CrudModel::appendNewRow()
{
int row = static_cast<int>(m_rowMapping.size());
int row = m_rowMapping.size();
beginInsertRows(QModelIndex(), row, row);
appendNewRowInternal();
endInsertRows();
}
void CrudModel::appendNewRowInternal()
{
m_rowMapping.emplace_back(allocNewRowKey(), std::vector<Value>(m_roData->cols()));
endInsertRows();
}
std::tuple<bool, QString> CrudModel::removeRows(const std::set<IntegerRange<int>> &row_ranges)

View file

@ -15,7 +15,6 @@
#include <vector>
#include <optional>
class PgAttribute;
class PgConstraint;
class OpenDatabase;
@ -211,7 +210,6 @@ 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;
@ -246,7 +244,6 @@ private:
std::tuple<bool, std::vector<Value>> saveRow(const PendingRow &pending_row);
void appendNewRow();
void appendNewRowInternal();
int lastRowKey = -1;
int allocNewRowKey() { return ++lastRowKey; }
@ -259,12 +256,10 @@ 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();
void connectionStateChanged(ASyncDBConnection::StateData state);
};
#endif // CRUDMODEL_H

View file

@ -1,10 +1,8 @@
#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[])
@ -30,8 +28,6 @@ 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,7 +21,6 @@ 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 \
@ -32,8 +31,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 \
@ -58,7 +57,6 @@ SOURCES += main.cpp\
serverinspector/DatabasesPage.cpp \
serverinspector/RolesPage.cpp \
serverinspector/ServerInspector.cpp \
util/Colors.cpp \
util/PgLabItemDelegate.cpp \
util/PgLabTableView.cpp \
util/SqlSyntaxHighlighter.cpp \
@ -93,8 +91,6 @@ SOURCES += main.cpp\
widgets/SingleRecordWidget.cpp
HEADERS += \
About.h \
Config.h \
catalog/delegates/IconColumnDelegate.h \
catalog/models/BaseTableModel.h \
catalog/models/ColumnTableModel.h \
@ -105,8 +101,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 \
@ -131,7 +127,6 @@ 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,7 +16,6 @@ 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
@ -68,35 +67,35 @@ if (role == Qt::DisplayRole) {
if (tt > 0.000000001f) {
float f = t / tt;
if (f > 0.9f) {
result = theme.explainTime[0];
result = QColor(255, 192, 192);
}
else if (f > 0.63f) {
result = theme.explainTime[1];
result = QColor(255, 224, 192);
}
else if (f > 0.36f) {
result = theme.explainTime[2];
result = QColor(255, 255, 192);
}
else if (f > 0.09f) {
result = theme.explainTime[3];
result = QColor(255, 255, 224);
}
else {
result = {};
result = QColor(Qt::white);
}
}
}
if (col == c_ColumnEstErr) {
float e = std::fabs(item->estimateError());
if (e > 1000.0f) {
result = theme.explainEstError[0];
result = QColor(255, 192, 192);
}
else if (e > 100.0f) {
result = theme.explainEstError[1];
result = QColor(255, 224, 192);
}
else if (e > 10.0f) {
result = theme.explainEstError[2];
result = QColor(255, 255, 192);
}
else {
result = {}; //QColor(Qt::white);
result = QColor(Qt::white);
}
}
}

View file

@ -2,7 +2,6 @@
#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.
@ -31,5 +30,4 @@ public:
private:
ExplainRoot::SPtr explain;
const ColorTheme &theme;
};

View file

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

View file

@ -322,14 +322,13 @@ void QueryTool::queryTextChanged()
setQueryTextChanged(true);
}
void QueryTool::connectionStateChanged()
void QueryTool::connectionStateChanged(ASyncDBConnection::StateData state)
{
QString iconname;
switch (m_dbConnection.state()) {
switch (state.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:
@ -587,9 +586,7 @@ void QueryTool::generateCode()
{
QString command = getCommand();
if (resultList.empty()) {
QMessageBox::question(this, "pglab", tr("Please execute the query first"),
QMessageBox::StandardButtons(QMessageBox::Ok),
QMessageBox::Ok);
QMessageBox::question(this, "pglab", tr("Please execute the query first"), 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();
void connectionStateChanged(ASyncDBConnection::StateData state);
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.itemView();
auto tv = m_databasesTableView.tableView();
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.itemView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
connect(m_databasesTableView.tableView()->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.itemView()->resizeColumnsToContents();
m_databasesTableView.tableView()->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.itemView();
auto tv = m_rolesTableView.tableView();
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.itemView()->resizeColumnsToContents();
m_rolesTableView.tableView()->resizeColumnsToContents();
}

View file

@ -1,165 +0,0 @@
#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;
}

View file

@ -1,50 +0,0 @@
#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,14 +1,10 @@
#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)
@ -86,8 +82,6 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
// }
// }
const ColorTheme &theme = GetColorTheme();
Oid oid = InvalidOid;
value = index.data(CustomDataTypeRole); // get OID
if (value.isValid())
@ -96,7 +90,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
value = index.data(CustomDataMeaningRole);
DataMeaning meaning = value.isValid()
? static_cast<DataMeaning>(value.toInt())
: DataMeaning::Normal;
: DataMeaningNormal;
value = index.data(Qt::DisplayRole);
@ -104,7 +98,9 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
if (value.isValid() && ! value.isNull()) {
QColor forground_color;
QColor forground_color = oid == Pgsql::bool_oid
? GetDefaultBoolColor(value.toBool())
: GetDefaultColorForType(oid);
option->features |= QStyleOptionViewItem::HasDisplay;
if (oid == Pgsql::bool_oid) {
@ -113,93 +109,82 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
option->text = FormatBoolForDisplay(b);
}
else {
forground_color = theme.GetColorForType(oid);
if (meaning == DataMeaning::Bytes) {
option->text = HandleBytes(value.toLongLong(), forground_color);
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) ;
}
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(theme.nullColor));
option->palette.setBrush(QPalette::Text, QBrush(GetDefaultNullColor()));
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;
}
QString PgLabItemDelegate::HandleBytes(qlonglong s, QColor &forground_color) const
{
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);
}
void PgLabItemDelegate::paint(QPainter *painter,
const QStyleOptionViewItem &option, const QModelIndex &index) const
const QStyleOptionViewItem &option, const QModelIndex &index) const
{
Q_ASSERT(index.isValid());
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());
}
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);
}
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,8 +29,7 @@ protected:
virtual void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const override;
private:
AbstractEditorFactory *m_editorFactory = nullptr;
QString HandleBytes(qlonglong s, QColor &forground_color) const;
AbstractEditorFactory *m_editorFactory = nullptr;
};
#endif // PGLABITEMDELEGATE_H

View file

@ -6,19 +6,14 @@
PgLabTableView::PgLabTableView(QWidget *parent)
: QTableView(parent)
{
setAlternatingRowColors(true);
setAlternatingRowColors(true);
setItemDelegate(new PgLabItemDelegate(this));
setWordWrap(false);
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 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);
auto vertical_header = verticalHeader();
vertical_header->setMinimumSectionSize(16);

View file

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

View file

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

481
pglabAll.pro.user.4.10-pre1 Normal file
View file

@ -0,0 +1,481 @@
<?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
bool waitForAndSendCommand();
void waitForAndSendCommand();
void doNewCommand();
void waitForResult();
@ -236,9 +236,7 @@ void ASyncDBConnectionThread::communicate()
if (m_state == ASyncDBConnection::State::Connected) {
if (!waitForAndSendCommand()) {
return;
}
waitForAndSendCommand();
}
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
@ -257,22 +255,15 @@ void ASyncDBConnectionThread::stop()
void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state)
{
qDebug() << "State change " + state.Message;
m_state = state.State;
Q_EMIT asyncConnObject->onStateChanged();
Q_EMIT asyncConnObject->onStateChanged(state);
}
bool ASyncDBConnectionThread::waitForAndSendCommand()
void 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_socket = whl.add(socket_event);
whl.add(m_stopEvent);
auto wait_result_stop = whl.add(m_stopEvent);
DWORD res = MsgWaitForMultipleObjectsEx(
whl.count(), // _In_ DWORD nCount,
@ -284,17 +275,8 @@ bool 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()
@ -302,6 +284,7 @@ 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
@ -376,11 +359,6 @@ 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
@ -435,7 +413,7 @@ void ASyncDBConnection::doStateCallback(State state)
m_connection.setNoticeReceiver(
[this](const PGresult *result) { processNotice(result); });
}
Q_EMIT onStateChanged();
Q_EMIT onStateChanged(state);
}

View file

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

View file

@ -19,24 +19,10 @@ namespace {
{ SslMode::verify_full, "verify-full" }
};
// 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;
inline const char *valuePtr(const std::string &v)
{
return v.empty() ? nullptr : v.c_str();
}
} // end unnamed namespace
@ -46,20 +32,21 @@ 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)
return e.mode;
result = e.mode;
return SslMode::allow;
return {};
}
ConnectionConfig::ConnectionConfig()
: m_parameters{{"application_name", QCoreApplication::applicationName()}}
: m_applicationName(QCoreApplication::applicationName().toUtf8().data())
{}
const ConnectionGroup *ConnectionConfig::parent() const
@ -105,131 +92,168 @@ const QString& ConnectionConfig::name() const
void ConnectionConfig::setHost(const QString& host)
{
setParameter(keywords.host, host);
if (m_host != host)
{
m_dirty = true;
m_host = std::move(host);
}
}
QString ConnectionConfig::host() const
const QString& ConnectionConfig::host() const
{
return getParameter(keywords.host);
return m_host;
}
void ConnectionConfig::setHostAddr(const QString &v)
{
setParameter(keywords.hostaddr, v);
if (m_hostaddr != v)
{
m_dirty = true;
m_hostaddr = std::move(v);
}
}
QString ConnectionConfig::hostAddr() const
const QString& ConnectionConfig::hostAddr() const
{
return getParameter(keywords.hostaddr);
return m_hostaddr;
}
void ConnectionConfig::setPort(unsigned short port)
{
setParameter(keywords.port, QString::number(port));
if (m_port != port)
{
m_dirty = true;
m_port = port;
}
}
unsigned short ConnectionConfig::port() const
{
QString s = getParameter(keywords.port);
if (s.isEmpty())
return 5432;
unsigned short port = static_cast<unsigned short>(s.toInt());
return port;
return m_port;
}
void ConnectionConfig::setUser(const QString& v)
{
setParameter(keywords.user, v);
if (m_user != v)
{
m_dirty = true;
m_user = v;
}
}
QString ConnectionConfig::user() const
const QString& ConnectionConfig::user() const
{
return getParameter(keywords.user);
return m_user;
}
void ConnectionConfig::setPassword(const QString& v)
{
setParameter(keywords.password, v);
if (m_password != v)
{
m_dirty = true;
m_password = v;
}
}
QString ConnectionConfig::password() const
const QString& ConnectionConfig::password() const
{
return getParameter(keywords.password);
return m_password;
}
void ConnectionConfig::setDbname(const QString& v)
{
setParameter(keywords.dbname, v);
if (m_dbname != v)
{
m_dirty = true;
m_dbname = v;
}
}
QString ConnectionConfig::dbname() const
const QString& ConnectionConfig::dbname() const
{
return getParameter(keywords.dbname);
return m_dbname;
}
void ConnectionConfig::setSslMode(SslMode m)
{
setParameter(keywords.sslmode, SslModeToString(m));
if (m_sslMode != m)
{
m_dirty = true;
m_sslMode = m;
}
}
SslMode ConnectionConfig::sslMode() const
{
QString s = getParameter(keywords.sslmode);
return StringToSslMode(s);
return m_sslMode;
}
void ConnectionConfig::setSslCert(const QString& v)
{
setParameter(keywords.sslcert, v);
if (m_sslCert != v)
{
m_dirty = true;
m_sslCert = std::move(v);
}
}
QString ConnectionConfig::sslCert() const
const QString& ConnectionConfig::sslCert() const
{
return getParameter(keywords.sslcert);
return m_sslCert;
}
void ConnectionConfig::setSslKey(const QString& v)
{
setParameter(keywords.sslkey, v);
if (m_sslKey != v)
{
m_dirty = true;
m_sslKey = std::move(v);
}
}
QString ConnectionConfig::sslKey() const
const QString& ConnectionConfig::sslKey() const
{
return getParameter(keywords.sslkey);
return m_sslKey;
}
void ConnectionConfig::setSslRootCert(const QString& v)
{
setParameter(keywords.sslrootcert, v);
if (m_sslRootCert != v)
{
m_dirty = true;
m_sslRootCert = std::move(v);
}
}
QString ConnectionConfig::sslRootCert() const
const QString& ConnectionConfig::sslRootCert() const
{
return getParameter(keywords.sslrootcert);
return m_sslRootCert;
}
void ConnectionConfig::setSslCrl(const QString& v)
{
setParameter(keywords.sslcrl, v);
if (m_sslCrl != v)
{
m_dirty = true;
m_sslCrl = std::move(v);
}
}
QString ConnectionConfig::sslCrl() const
const QString& ConnectionConfig::sslCrl() const
{
return getParameter(keywords.sslcrl);
return m_sslCrl;
}
// 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::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::dirty() const
{
@ -248,7 +272,7 @@ QString ConnectionConfig::makeLongDescription() const
return result;
}
const QByteArray& ConnectionConfig::encodedPassword() const
QByteArray ConnectionConfig::encodedPassword() const
{
return m_encodedPassword;
}
@ -290,76 +314,58 @@ QString ConnectionConfig::escapeConnectionStringValue(const QString &value)
QString ConnectionConfig::connectionString() const
{
QString s;
for (auto && param : m_parameters)
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())
{
// 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())
s += " sslcert=";
s += escapeConnectionStringValue(m_sslCert);
}
if (!m_sslKey.isEmpty())
{
if (m_parameters.erase(name) > 0)
{
m_dirty = true;
}
}
else
s += " sslkey=";
s += escapeConnectionStringValue(m_sslKey);
}
if (!m_sslRootCert.isEmpty())
{
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;
}
}
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);
//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;
return s;
}
void ConnectionConfig::writeToEnvironment(QProcessEnvironment &env) const
{
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, "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, "PGSSLCOMPRESSION", "0");
strToEnv(env, "PGCONNECT_TIMEOUT", "10");
env.insert("PGCLIENTENCODING", "utf8");

View file

@ -6,7 +6,6 @@
#include <QMetaType>
#include <QUuid>
#include <QVector>
#include <map>
#include <vector>
#include <string>
@ -46,7 +45,6 @@ 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.
@ -75,42 +73,42 @@ public:
const QString& name() const;
void setHost(const QString& host);
QString host() const;
const QString& host() const;
void setHostAddr(const QString& v);
QString hostAddr() const;
const QString& hostAddr() const;
void setPort(unsigned short port);
unsigned short port() const;
void setUser(const QString& v);
QString user() const;
const QString& user() const;
void setPassword(const QString& v);
QString password() const;
const QString& password() const;
void setDbname(const QString& v);
QString dbname() const;
const QString& dbname() const;
void setSslMode(SslMode m);
SslMode sslMode() const;
void setSslCert(const QString& v);
QString sslCert() const;
void setSslCert(const QString& v);
const QString& sslCert() const;
void setSslKey(const QString& v);
QString sslKey() const;
void setSslKey(const QString& v);
const QString& sslKey() const;
void setSslRootCert(const QString& v);
QString sslRootCert() const;
void setSslRootCert(const QString& v);
const QString& sslRootCert() const;
void setSslCrl(const QString& v);
QString sslCrl() const;
void setSslCrl(const QString& v);
const 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;
@ -120,7 +118,7 @@ public:
bool operator==(QUuid id) const { return m_uuid == id; }
QString makeLongDescription() const;
const QByteArray& encodedPassword() const;
QByteArray encodedPassword() const;
void setEncodedPassword(const QByteArray &encodedPassword);
// void write(QDataStream &out) const;
@ -132,35 +130,26 @@ 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,15 +24,14 @@ 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 static_cast<int>(m_paramList.size());
return m_paramList.size();
}
int ParamListModel::columnCount(const QModelIndex &) const

View file

@ -82,6 +82,16 @@ 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,43 +3,18 @@
#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() && identity == '\0') {
if (isSerial()) {
if (typid == Pgsql::int4_oid)
type_str = "SERIAL";
else if (typid == Pgsql::int8_oid)
@ -60,31 +35,16 @@ QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
if (notnull)
sql += " NOT NULL";
auto identity = getIdentity();
auto generated = getGenerated();
if (identity != Identity::None) {
sql += " GENERATED ";
if (identity == Identity::Always)
sql += "ALWAYS";
else if (identity == Identity::ByDefault)
sql += "BY DEFAULT";
sql += " AS IDENTITY";
if (hasdef && !isSerial())
sql += " DEFAULT " % defaultValue;
auto sequence = cat.sequences()->getByObjectNsAndName(serschema, sername);
auto options_string = sequence->nonDefaultOptionsString();
if (!options_string.isEmpty())
sql += "(" % options_string % ")";
if (identity != '\0') {
sql += " GENERATED ";
if (identity == 'a') sql += "ALWAYS";
else if (identity == 'd') sql += "BY DEFAULT";
sql += " AS IDENTITY";
}
else if (generated != Generated::None)
{
if (generated == Generated::Stored)
sql += " GENERATED ALWAYS AS " % defaultValue % " STORED";
}
else
{
if (hasdef && !isSerial())
sql += " DEFAULT " % defaultValue;
}
// TODO sequence options might be missing
return sql;
}

View file

@ -11,29 +11,7 @@ class PgDatabaseCatalog;
class PgAttribute {
public:
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;
};
using Key = std::tuple<Oid, int16_t>;
Oid relid = InvalidOid;
QString name;
@ -45,36 +23,19 @@ 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
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==(Key _k) const { return relid == std::get<0>(_k) && num == std::get<1>(_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,14 +16,12 @@ SELECT attrelid, attname, atttypid, attstattarget,
cs.relname AS sername, ns.nspname AS serschema, d.description)__";
if (m_catalog.serverVersion() >= 100000)
q += ", attidentity";
if (m_catalog.serverVersion() >= 120000)
q += ", attgenerated";
q += R"__(
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;
}
@ -38,8 +36,6 @@ 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,5 +1,4 @@
#include "PgClass.h"
#include "PgAttribute.h"
#include "PgAttributeContainer.h"
#include "PgClassContainer.h"
#include "PgDatabaseCatalog.h"
@ -7,8 +6,7 @@
#include "PgInheritsContainer.h"
#include <QStringBuilder>
#include "SqlFormattingUtils.h"
#include <ranges>
#include <cassert>
void operator<<(RelPersistence &s, const Pgsql::Value &v)
@ -35,60 +33,33 @@ 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 'p':
s = RelKind::PartitionedTable;
break;
case 'I':
s = RelKind::PartitionedIndex;
break;
default:
throw std::runtime_error("Unknown RelKind");
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;
}
}
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())
@ -96,7 +67,6 @@ QString PgClass::createSql() const
switch (kind)
{
case RelKind::Table:
case RelKind::PartitionedTable:
createSqlCache = createTableSql();
break;
case RelKind::View:
@ -111,16 +81,14 @@ 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::PartitionedTable: return "PARTITIONED TABLE";
case RelKind::PartitionedIndex: return "PARTITIONED INDEX";
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";
}
throw std::runtime_error("Unexpected value in PgClass::typeName()");
}
@ -133,18 +101,7 @@ QString PgClass::aclAllPattern() const
default:
break;
}
return {};
}
QString PgClass::ddlTypeName() const
{
switch (kind)
{
case RelKind::PartitionedTable:
return "TABLE";
default:
return PgNamespaceObject::ddlTypeName();
}
return {};
}
QString PgClass::createTableSql() const
@ -158,92 +115,52 @@ QString PgClass::createTableSql() const
sql += "TEMP ";
sql += "TABLE ";
sql += fullyQualifiedQuotedObjectName();
sql += " (\n ";
if (!partitionBoundaries.isEmpty())
auto && cols = catalog().attributes()->getColumnsForRelation(oid());
bool first = true;
for (auto && col : cols)
{
sql += " PARTITION OF " + getPartitionOfName();
sql += generateBodySql(true);
}
else
sql += generateBodySql(false);
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 += generateInheritsSql()
% partitionBySql()
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 } ]
% 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());
@ -262,59 +179,6 @@ 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)
@ -341,15 +205,4 @@ 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,9 +5,6 @@
#include "PgNamespaceObject.h"
#include <QString>
#include <libpq-fe.h>
#include <boost/container/small_vector.hpp>
class PgAttribute;
enum class RelPersistence {
Permanent, // p
@ -17,8 +14,7 @@ enum class RelPersistence {
void operator<<(RelPersistence &s, const Pgsql::Value &v);
enum class RelKind
{
enum class RelKind {
Table, // r
Index, // i
Sequence, // S
@ -26,44 +22,11 @@ enum class RelKind
MaterializedView, // m
Composite, // c
Toast, // t
ForeignTable, // f
PartitionedTable, // p
PartitionedIndex // I
ForeignTable // f
};
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:
@ -84,8 +47,6 @@ public:
int minmxid;
std::vector<QString> options;
QString viewdef;
QString partitionBoundaries;
PgPartitionedTable partitionedTable; // ignore if RelKind != PartitionedTable
using PgNamespaceObject::PgNamespaceObject;
@ -94,22 +55,11 @@ 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,6 +1,8 @@
#include "PgClassContainer.h"
#include "Pgsql_Connection.h"
#include "Pgsql_Col.h"
#include "PgDatabaseCatalog.h"
#include "PgNamespaceContainer.h"
#include <iterator>
std::string PgClassContainer::getLoadQuery() const
@ -12,58 +14,17 @@ 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";
if (minimumVersion(100000))
q += " LEFT JOIN pg_partitioned_table AS pt ON (partrelid=pg_class.oid) \n";
q +=
" LEFT JOIN pg_catalog.pg_description AS d ON (objoid=pg_class.oid AND objsubid=0) \n"
"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);
@ -72,7 +33,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
@ -92,33 +53,5 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
if (lessThenVersion(120000))
col >> v.hasoids;
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;
return v;
}

View file

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

View file

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

View file

@ -4,7 +4,6 @@
#include "Pgsql_declare.h"
#include "Pgsql_Result.h"
#include <QString>
#include <functional>
#include <memory>
#include <vector>
#include <libpq-fe.h>
@ -69,15 +68,6 @@ 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);
@ -88,36 +78,6 @@ 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

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