From bb0e08461a5bee9a587ddbd942d5d134dc9fddab Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 3 Sep 2022 09:36:04 +0200
Subject: [PATCH 01/64] cleanup
---
pglablib/catalog/PgAcl.cpp | 10 ----------
pglablib/catalog/PgServerObject.cpp | 1 -
2 files changed, 11 deletions(-)
diff --git a/pglablib/catalog/PgAcl.cpp b/pglablib/catalog/PgAcl.cpp
index d7d2938..b3f46ae 100644
--- a/pglablib/catalog/PgAcl.cpp
+++ b/pglablib/catalog/PgAcl.cpp
@@ -82,16 +82,6 @@ QString PgAcl::singleString() const
void operator<<(PgAcl &acl, const Pgsql::Value &v)
{
acl.setFromString(v);
-// const char *c = v.c_str();
-// Pgsql::ArrayParser parser(c);
-
-// while (true) {
-// auto elem = parser.GetNextElem();
-// if (!elem.ok)
-// break;
-// auto&& v = elem.value.value_or("");
-// s.emplace_back(QString(v.data()));
-// }
}
namespace Pgsql {
diff --git a/pglablib/catalog/PgServerObject.cpp b/pglablib/catalog/PgServerObject.cpp
index 07f8284..2e4ea4a 100644
--- a/pglablib/catalog/PgServerObject.cpp
+++ b/pglablib/catalog/PgServerObject.cpp
@@ -1,5 +1,4 @@
#include "PgServerObject.h"
-#include "ArrayParser.h"
#include "PgAuthIdContainer.h"
#include "PgDatabaseCatalog.h"
#include "SqlFormattingUtils.h"
From f8f06232b90db8aa0a0b06197b9ad9feb27bbbe3 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 3 Sep 2022 09:41:12 +0200
Subject: [PATCH 02/64] Start using reno
---
releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
diff --git a/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml b/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
new file mode 100644
index 0000000..f9de565
--- /dev/null
+++ b/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Started using OpenStack Reno for release notes.
From 4e1d128ee92356491365183e0164e65aeeac9e34 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 3 Sep 2022 12:43:16 +0200
Subject: [PATCH 03/64] fix header include path
---
pglab/querytool/QueryTab.ui | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pglab/querytool/QueryTab.ui b/pglab/querytool/QueryTab.ui
index 7d5ef68..da957c3 100644
--- a/pglab/querytool/QueryTab.ui
+++ b/pglab/querytool/QueryTab.ui
@@ -217,7 +217,7 @@
CodeEditor
QPlainTextEdit
-
+
From c874b297c17e7d60234afc01ba46128d1e4a8d85 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 3 Sep 2022 12:44:32 +0200
Subject: [PATCH 04/64] Add about dialog to File menu of the connection
manager.
Make the contents of the dialog more accurate and include a link to the releasenotes file.
---
pglab/About.cpp | 23 +++++++++++++++++++
pglab/About.h | 5 ++++
pglab/ConnectionManagerWindow.cpp | 7 ++++++
pglab/ConnectionManagerWindow.h | 2 ++
pglab/ConnectionManagerWindow.ui | 13 ++++++++++-
pglab/DatabaseWindow.cpp | 15 ++----------
pglab/pglab.pro | 2 ++
.../notes/about-7eeb6e282f8e2ef1.yaml | 13 +++++++++++
8 files changed, 66 insertions(+), 14 deletions(-)
create mode 100644 pglab/About.cpp
create mode 100644 pglab/About.h
create mode 100644 releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
diff --git a/pglab/About.cpp b/pglab/About.cpp
new file mode 100644
index 0000000..e5c3753
--- /dev/null
+++ b/pglab/About.cpp
@@ -0,0 +1,23 @@
+#include "About.h"
+#include
+#include
+#include
+
+void ShowAboutDialog(QWidget *parent)
+{
+ QString pgLabVersionString = "0.2";
+ QString releaseNotesUrl = "file:///" + QCoreApplication::applicationDirPath() + "\\releasenotes.html";
+ QString year = QString::fromLatin1(__DATE__, 11).right(4);
+ QMessageBox::about(parent, "pgLab " + pgLabVersionString, QMessageBox::tr(
+ "Version %3
"
+ "Releasenotes
"
+ "Copyrights 2016-%1, Eelke Klein, All Rights Reserved.
"
+ "The program is provided AS IS with NO WARRANTY OF ANY KIND, "
+ "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS "
+ "FOR A PARTICULAR PURPOSE.
"
+ "This program is dynamically linked with Qt " QT_VERSION_STR " Copyrights "
+ "the Qt Company Ltd. https://www.qt.io/licensing/.
"
+ "Some icons by Icons8 and others by fatcow provided under Creative Commons "
+ "attribution 3.0 license.
"
+ ).arg(year).arg(releaseNotesUrl).arg(pgLabVersionString));
+}
diff --git a/pglab/About.h b/pglab/About.h
new file mode 100644
index 0000000..1274f37
--- /dev/null
+++ b/pglab/About.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include
+
+void ShowAboutDialog(QWidget *parent);
diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp
index 0e57c48..10bdc06 100644
--- a/pglab/ConnectionManagerWindow.cpp
+++ b/pglab/ConnectionManagerWindow.cpp
@@ -1,4 +1,5 @@
#include "ConnectionManagerWindow.h"
+#include "About.h"
#include "ui_ConnectionManagerWindow.h"
#include "MasterController.h"
#include "ConnectionController.h"
@@ -102,3 +103,9 @@ void ConnectionManagerWindow::on_actionConfigureCopy_triggered()
auto ci = ui->treeView->selectionModel()->currentIndex();
m_connectionController->editCopy(ci);
}
+
+void ConnectionManagerWindow::on_actionAbout_triggered()
+{
+ ShowAboutDialog(this);
+}
+
diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h
index b740ed1..8ad70b3 100644
--- a/pglab/ConnectionManagerWindow.h
+++ b/pglab/ConnectionManagerWindow.h
@@ -39,6 +39,8 @@ private slots:
void on_actionConfigureCopy_triggered();
+ void on_actionAbout_triggered();
+
private:
Ui::ConnectionManagerWindow *ui;
MasterController *m_masterController;
diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui
index 64ab8e6..6eee4e4 100644
--- a/pglab/ConnectionManagerWindow.ui
+++ b/pglab/ConnectionManagerWindow.ui
@@ -26,13 +26,14 @@
0
0
413
- 30
+ 29
@@ -164,6 +165,16 @@ QToolButton {
Configure copy
+
+
+
+ :/icons/about.png
+
+
+
+ About
+
+
diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp
index d00a925..57908f6 100644
--- a/pglab/DatabaseWindow.cpp
+++ b/pglab/DatabaseWindow.cpp
@@ -1,4 +1,5 @@
#include "DatabaseWindow.h"
+#include "About.h"
#include "ui_DatabaseWindow.h"
#include "util.h"
#include "crud/CrudTab.h"
@@ -305,19 +306,7 @@ void DatabaseWindow::dropEvent(QDropEvent *event)
void DatabaseWindow::on_actionAbout_triggered()
{
- QMessageBox::about(this, "pgLab 0.1", tr(
- "Copyrights 2016-2019, Eelke Klein, All Rights Reserved.\n"
- "\n"
- "The program is provided AS IS with NO WARRANTY OF ANY KIND, "
- "INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS "
- "FOR A PARTICULAR PURPOSE.\n"
- "\n"
- "This program is dynamically linked with Qt 5.12 Copyright (C) 2018 "
- "The Qt Company Ltd. https://www.qt.io/licensing/. \n"
- "\n"
- "Icons by fatcow http://www.fatcow.com/free-icons provided under Creative Commons "
- "attribution 3.0 license."
- ));
+ ShowAboutDialog(this);
}
diff --git a/pglab/pglab.pro b/pglab/pglab.pro
index 916109e..3bc5b30 100644
--- a/pglab/pglab.pro
+++ b/pglab/pglab.pro
@@ -21,6 +21,7 @@ DEFINES += _WIN32_WINNT=0x0501
win32:RC_ICONS += pglab.ico
SOURCES += main.cpp\
+ About.cpp \
catalog/delegates/IconColumnDelegate.cpp \
catalog/models/BaseTableModel.cpp \
catalog/models/ColumnTableModel.cpp \
@@ -91,6 +92,7 @@ SOURCES += main.cpp\
widgets/SingleRecordWidget.cpp
HEADERS += \
+ About.h \
catalog/delegates/IconColumnDelegate.h \
catalog/models/BaseTableModel.h \
catalog/models/ColumnTableModel.h \
diff --git a/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
new file mode 100644
index 0000000..54555b8
--- /dev/null
+++ b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - The connection window now has an About item in the file menu.
+ - The about dialog now uses html for enhanced formatting and hyperlinks.
+ - |
+ The about dialog now declares the usage of Icons8 icons and provides a
+ link to icons8.com in accordance with the free usage conditions.
+ - |
+ The about dialog now shows the Qt version it was actually build with instead of
+ a hardcoded value.
+ - |
+ The about dialog now contains a link to the releasenotes.html file which will be
+ opened in the users browser.
From 1d53a6397b377cc0c96b55b42f9df338ff83540e Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 09:26:35 +0200
Subject: [PATCH 05/64] There is no master branch it is called main.
---
.gitlab-ci.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 7070637..9f46603 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -16,7 +16,7 @@ test-docs:
only:
- branches
except:
- - master
+ - main
pages:
stage: deploy
@@ -27,4 +27,4 @@ pages:
paths:
- public
only:
- - master
+ - main
From a8009a1c372a0d9b2a873ce8f79519433ca753ac Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Sun, 4 Sep 2022 07:40:14 +0000
Subject: [PATCH 06/64] Change docs theme
---
docs/conf.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
index 909c7bd..1673902 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,7 @@
# -- Project information -----------------------------------------------------
project = 'pgLab User Manual'
-copyright = '2021, Eelke Klein'
+copyright = '2021-2022, Eelke Klein'
author = 'Eelke Klein'
# The full version, including alpha/beta/rc tags
@@ -47,7 +47,7 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = 'alabaster'
+html_theme = 'PD'
# 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,
From 6631d1df5d71b98122e9e0a711e96eae6041238c Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Sun, 4 Sep 2022 07:43:35 +0000
Subject: [PATCH 07/64] fix building of docs
---
.gitlab-ci.yml | 2 ++
docs/conf.py | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9f46603..4558dee 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,7 @@ test-docs:
stage: test
script:
- pip install -U sphinx
+ - pip install -U sphinx-theme-pd
- sphinx-build -b html docs public
only:
- branches
@@ -22,6 +23,7 @@ pages:
stage: deploy
script:
- pip install -U sphinx
+ - pip install -U sphinx-theme-pd
- sphinx-build -b html docs public
artifacts:
paths:
diff --git a/docs/conf.py b/docs/conf.py
index 1673902..785f808 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -47,7 +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 = 'PD'
+import sphinx_theme_pd
+html_theme = 'sphinx_theme_pd'
+html_theme_path = [sphinx_theme_pd.get_html_theme_path()]
# 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,
From 61346ae95df167e64f0fd3a34c92fce8c422ff97 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 12:41:37 +0200
Subject: [PATCH 08/64] Remove BUILD because its contents is completely out of
date.
---
BUILD | 18 ------------------
1 file changed, 18 deletions(-)
delete mode 100644 BUILD
diff --git a/BUILD b/BUILD
deleted file mode 100644
index 3f4f16a..0000000
--- a/BUILD
+++ /dev/null
@@ -1,18 +0,0 @@
-
-If it doesn't detect Qt CMAKE_PREFIX_PATH needs to be set
-
-export CMAKE_PREFIX_PATH=/usr/lib/x86_64-linux-gnu/qt5/mkspecs/features/data/cmake
-
-Dependencies:
-- boost (asio, system, ???)
-- Botan-2
-- libpq
-- Qt5
-- fmt (should we include this one in the project?
-
-
-- jsoncpp (included)
-
-On ubuntu
-
-sudo apt install libpq-dev libboost1.63-all-dev
From 6b8c54398d4a686ba4cb3911c5fef90a96766881 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 12:43:09 +0200
Subject: [PATCH 09/64] Add installation and releasenotes sections to the user
manual.
---
.gitignore | 1 +
.gitlab-ci.yml | 27 ++++++++++++-------
docs/conf.py | 5 ++--
docs/index.rst | 7 +++--
docs/installation.rst | 16 +++++++++++
docs/releasenotes.rst | 5 ++++
pglab/About.cpp | 2 +-
pglab/ConnectionManagerWindow.cpp | 14 +++++++---
pglab/ConnectionManagerWindow.h | 2 ++
pglab/ConnectionManagerWindow.ui | 6 +++++
releasenotes/config.yaml | 2 ++
...include-releasenotes-53aca515c5c124fd.yaml | 4 +++
12 files changed, 71 insertions(+), 20 deletions(-)
create mode 100644 docs/installation.rst
create mode 100644 docs/releasenotes.rst
create mode 100644 releasenotes/config.yaml
create mode 100644 releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
diff --git a/.gitignore b/.gitignore
index 645129f..35dfb13 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,3 +11,4 @@ srcdoc/
pglabAll.pro.user.4.8-pre1
*.user
/pglabAll.pro.user*
+/public/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 4558dee..9c157d6 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,25 +8,34 @@ cache:
- "$CI_PROJECT_DIR/pip-cache"
key: "$CI_PROJECT_ID"
+.install_sphinx:
+ before_script:
+ - pip install -U sphinx
+ - pip install -U sphinx-theme-pd
+ - pip install -U reno[sphinx]
+
+
test-docs:
stage: test
+ extends:
+ - .install_sphinx
script:
- - pip install -U sphinx
- - pip install -U sphinx-theme-pd
- - sphinx-build -b html docs public
+ - sphinx-build -b html docs userdocs
+ artifacts:
+ paths:
+ - userdocs
only:
- branches
- except:
- - main
-pages:
+deploy:
stage: deploy
+ extends:
+ - .install_sphinx
script:
- - pip install -U sphinx
- - pip install -U sphinx-theme-pd
- sphinx-build -b html docs public
artifacts:
paths:
- public
only:
- - main
+ - tags
+
diff --git a/docs/conf.py b/docs/conf.py
index 785f808..6ce7a1e 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -22,7 +22,7 @@ copyright = '2021-2022, Eelke Klein'
author = 'Eelke Klein'
# The full version, including alpha/beta/rc tags
-release = 'https://eelke.gitlab.io/pgLab/'
+# release = '1.0'
# -- General configuration ---------------------------------------------------
@@ -31,6 +31,7 @@ release = 'https://eelke.gitlab.io/pgLab/'
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
+'reno.sphinxext'
]
# Add any paths that contain templates here, relative to this directory.
@@ -54,4 +55,4 @@ html_theme_path = [sphinx_theme_pd.get_html_theme_path()]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = []
diff --git a/docs/index.rst b/docs/index.rst
index 08e8642..ff59f1d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -10,13 +10,12 @@ pgLab User Manual
:maxdepth: 2
:caption: Contents:
+ releasenotes
+ installation
+
Indices and tables
==================
* :ref:`genindex`
* :ref:`search`
-Fork this project
-==================
-
-* https://eelke.gitlab.io/pgLab/
diff --git a/docs/installation.rst b/docs/installation.rst
new file mode 100644
index 0000000..9a416a9
--- /dev/null
+++ b/docs/installation.rst
@@ -0,0 +1,16 @@
+============
+Installation
+============
+
+Currently only binaries for Windows 64-bit are provided.
+
+-------
+Windows
+-------
+
+Downloads can be found `here `_.
+
+Unpack the contents of the 7zip archive to a folder of your choosing for instance
+`C:\\Program files\\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 `_.
diff --git a/docs/releasenotes.rst b/docs/releasenotes.rst
new file mode 100644
index 0000000..9d6d0ae
--- /dev/null
+++ b/docs/releasenotes.rst
@@ -0,0 +1,5 @@
+============
+Releasenotes
+============
+
+.. release-notes::
diff --git a/pglab/About.cpp b/pglab/About.cpp
index e5c3753..a38b4b1 100644
--- a/pglab/About.cpp
+++ b/pglab/About.cpp
@@ -8,7 +8,7 @@ void ShowAboutDialog(QWidget *parent)
QString pgLabVersionString = "0.2";
QString releaseNotesUrl = "file:///" + QCoreApplication::applicationDirPath() + "\\releasenotes.html";
QString year = QString::fromLatin1(__DATE__, 11).right(4);
- QMessageBox::about(parent, "pgLab " + pgLabVersionString, QMessageBox::tr(
+ QMessageBox::about(parent, "pgLab", QMessageBox::tr(
"Version %3
"
"Releasenotes
"
"Copyrights 2016-%1, Eelke Klein, All Rights Reserved.
"
diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp
index 10bdc06..5377631 100644
--- a/pglab/ConnectionManagerWindow.cpp
+++ b/pglab/ConnectionManagerWindow.cpp
@@ -3,13 +3,13 @@
#include "ui_ConnectionManagerWindow.h"
#include "MasterController.h"
#include "ConnectionController.h"
+#include "ConnectionListModel.h"
+#include
+#include
#include
#include
-#include "ConnectionListModel.h"
-
-
-#include
#include
+#include
ConnectionManagerWindow::ConnectionManagerWindow(MasterController *master, QWidget *parent)
: QMainWindow(parent)
@@ -109,3 +109,9 @@ void ConnectionManagerWindow::on_actionAbout_triggered()
ShowAboutDialog(this);
}
+
+void ConnectionManagerWindow::on_actionManual_triggered()
+{
+ QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
+}
+
diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h
index 8ad70b3..680915d 100644
--- a/pglab/ConnectionManagerWindow.h
+++ b/pglab/ConnectionManagerWindow.h
@@ -41,6 +41,8 @@ private slots:
void on_actionAbout_triggered();
+ void on_actionManual_triggered();
+
private:
Ui::ConnectionManagerWindow *ui;
MasterController *m_masterController;
diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui
index 6eee4e4..2d52b88 100644
--- a/pglab/ConnectionManagerWindow.ui
+++ b/pglab/ConnectionManagerWindow.ui
@@ -34,6 +34,7 @@
Fi&le
+
@@ -175,6 +176,11 @@ QToolButton {
About
+
+
+ Manual
+
+
diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml
new file mode 100644
index 0000000..fa9f8f3
--- /dev/null
+++ b/releasenotes/config.yaml
@@ -0,0 +1,2 @@
+---
+encoding: utf8
diff --git a/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml b/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
new file mode 100644
index 0000000..88d25e6
--- /dev/null
+++ b/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ Release notes are now included in the user manual.
From f3025928a3d2cfe686ad324fa23a73e1ee6a66be Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 12:59:46 +0200
Subject: [PATCH 10/64] fix pipeline needing gcc for dulwich
---
.gitlab-ci.yml | 5 +----
docs/requirements.txt | 5 +++++
2 files changed, 6 insertions(+), 4 deletions(-)
create mode 100644 docs/requirements.txt
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9c157d6..2e87e97 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -10,10 +10,7 @@ cache:
.install_sphinx:
before_script:
- - pip install -U sphinx
- - pip install -U sphinx-theme-pd
- - pip install -U reno[sphinx]
-
+ - pip install -U -r docs/requirements.txt
test-docs:
stage: test
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..f828e1c
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,5 @@
+reno[sphinx]==3.5.0
+dulwich --global-option='--pure'
+Sphinx==5.1.1
+sphinx-theme-pd==0.8.3
+
From c748b267a66af4a1edbbae76506ba0523dfbcb54 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 13:28:08 +0200
Subject: [PATCH 11/64] fix pipeline
installing python packages without gcc seems to be very hard
also fix pip caching
---
.gitlab-ci.yml | 5 +++--
docs/requirements.txt | 1 -
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2e87e97..8562ad8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,12 +5,13 @@ variables:
cache:
paths:
- - "$CI_PROJECT_DIR/pip-cache"
+ - "$PIP_CACHE_DIR"
key: "$CI_PROJECT_ID"
.install_sphinx:
before_script:
- - pip install -U -r docs/requirements.txt
+ - apk add gcc musl-dev
+ - pip install --cache-dir "$PIP_CACHE_DIR" -U -r docs/requirements.txt
test-docs:
stage: test
diff --git a/docs/requirements.txt b/docs/requirements.txt
index f828e1c..2fc0c18 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,4 @@
reno[sphinx]==3.5.0
-dulwich --global-option='--pure'
Sphinx==5.1.1
sphinx-theme-pd==0.8.3
From f8528143ac937c164a9924a9bcbb7924730016d2 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 4 Sep 2022 13:32:38 +0200
Subject: [PATCH 12/64] these items were not really features
---
releasenotes/notes/about-7eeb6e282f8e2ef1.yaml | 2 +-
.../notes/docs-include-releasenotes-53aca515c5c124fd.yaml | 2 +-
releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
index 54555b8..33e7627 100644
--- a/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
+++ b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
@@ -1,5 +1,5 @@
---
-features:
+other:
- The connection window now has an About item in the file menu.
- The about dialog now uses html for enhanced formatting and hyperlinks.
- |
diff --git a/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml b/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
index 88d25e6..9876f2c 100644
--- a/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
+++ b/releasenotes/notes/docs-include-releasenotes-53aca515c5c124fd.yaml
@@ -1,4 +1,4 @@
---
-features:
+other:
- |
Release notes are now included in the user manual.
diff --git a/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml b/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
index f9de565..33e37dd 100644
--- a/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
+++ b/releasenotes/notes/start-using-reno-2ae006d35e966cce.yaml
@@ -1,4 +1,4 @@
---
-features:
+other:
- |
Started using OpenStack Reno for release notes.
From 4fa2189b273a8d1324dd35c48a4e7dfcec48fb5a Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 07:33:08 +0200
Subject: [PATCH 13/64] Added the capability to reset the password manager
Also some documentation about the password manager.
---
core/PasswordManager.cpp | 17 ++-
core/PasswordManager.h | 72 +++++++------
docs/connectionmanager/index.rst | 7 ++
docs/connectionmanager/passwordmanager.rst | 35 ++++++
docs/index.rst | 4 +-
docs/installation.rst | 12 ++-
docs/internals.rst | 11 ++
pglab/ConnectionController.cpp | 100 +++++++++++-------
pglab/ConnectionController.h | 3 +-
pglab/ConnectionListModel.cpp | 12 ++-
pglab/ConnectionListModel.h | 1 +
pglab/ConnectionManagerWindow.cpp | 12 +++
pglab/ConnectionManagerWindow.h | 2 +
pglab/ConnectionManagerWindow.ui | 16 ++-
pglablib/ConnectionConfig.h | 1 +
releasenotes/config.yaml | 8 ++
...eset-master-password-6a8f5ccf5a052344.yaml | 5 +
17 files changed, 233 insertions(+), 85 deletions(-)
create mode 100644 docs/connectionmanager/index.rst
create mode 100644 docs/connectionmanager/passwordmanager.rst
create mode 100644 docs/internals.rst
create mode 100644 releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
diff --git a/core/PasswordManager.cpp b/core/PasswordManager.cpp
index 1e3eb1e..b398ba1 100644
--- a/core/PasswordManager.cpp
+++ b/core/PasswordManager.cpp
@@ -156,7 +156,19 @@ void PasswordManager::closeDatabase()
bool PasswordManager::locked() const
{
- return m_cryptoEngine == nullptr;
+ return m_cryptoEngine == nullptr;
+}
+
+void PasswordManager::resetMasterPassword(QSqlDatabase &db)
+{
+ if (!isPskStoreInitialized(db))
+ return;
+
+ closeDatabase();
+ QSqlQuery del_algo("DELETE FROM " + m_secretAlgoTableName + " WHERE id=1", db);
+ del_algo.exec();
+ QSqlQuery del_hash("DELETE FROM " + m_secretHashTableName + " WHERE id=1", db);
+ del_hash.exec();
}
std::string PasswordManager::encrypt(const std::string &name, const std::string &passwd)
@@ -246,7 +258,7 @@ bool PasswordManager::isPskStoreInitialized(QSqlDatabase& db)
return false;
}
- QSqlQuery sel_algo("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1", db);
+ QSqlQuery sel_algo("SELECT algo FROM " + m_secretAlgoTableName + " WHERE id=1", db);
if (!sel_algo.next()) {
return false;
}
@@ -297,3 +309,4 @@ KeyStrengthener PasswordManager::createKeyStrengthener()
key_size
);
}
+
diff --git a/core/PasswordManager.h b/core/PasswordManager.h
index 895cdc4..cf079d2 100644
--- a/core/PasswordManager.h
+++ b/core/PasswordManager.h
@@ -14,64 +14,66 @@
namespace Botan {
- class Encrypted_PSK_Database;
- class PasswordHash;
+ class Encrypted_PSK_Database;
+ class PasswordHash;
}
class PasswordManagerException: public std::exception {
public:
- using std::exception::exception; //(char const* const _Message);
+ using std::exception::exception; //(char const* const _Message);
};
class PasswordManagerLockedException: public PasswordManagerException {
public:
- using PasswordManagerException::PasswordManagerException;
+ using PasswordManagerException::PasswordManagerException;
};
class PasswordCryptoEngine;
class PasswordManager {
public:
- enum Result {
- Ok,
- Locked,
- Error
- };
+ enum Result {
+ Ok,
+ Locked,
+ Error
+ };
- PasswordManager();
- ~PasswordManager();
+ 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;
+ /** 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;
+ void resetMasterPassword(QSqlDatabase &db);
- std::string encrypt(const std::string &id, const std::string &passwd);
- std::string decrypt(const std::string &id, const std::string_view &encpwd);
+
+ 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 m_cryptoEngine;
+ QString m_passwordTableName = "psk_passwd";
+ QString m_secretAlgoTableName = "psk_masterkey_algo";
+ QString m_secretHashTableName = "psk_masterkey_hash";
+ std::unique_ptr m_cryptoEngine;
- bool isPskStoreInitialized(QSqlDatabase& db);
- void initializeNewPskStore(QSqlDatabase &db);
+ bool isPskStoreInitialized(QSqlDatabase& db);
+ void initializeNewPskStore(QSqlDatabase &db);
- /// Get PasswordHash from parameters in database
- KeyStrengthener getKeyStrengthener(QSqlDatabase &db);
- KeyStrengthener createKeyStrengthener();
+ /// Get PasswordHash from parameters in database
+ KeyStrengthener getKeyStrengthener(QSqlDatabase &db);
+ KeyStrengthener createKeyStrengthener();
- std::tuple, Botan::secure_vector>
- deriveKey(KeyStrengthener &ks, QString passphrase);
+ std::tuple, Botan::secure_vector>
+ deriveKey(KeyStrengthener &ks, QString passphrase);
};
diff --git a/docs/connectionmanager/index.rst b/docs/connectionmanager/index.rst
new file mode 100644
index 0000000..909e32f
--- /dev/null
+++ b/docs/connectionmanager/index.rst
@@ -0,0 +1,7 @@
+==================
+Connection Manager
+==================
+
+.. toctree::
+
+ passwordmanager
diff --git a/docs/connectionmanager/passwordmanager.rst b/docs/connectionmanager/passwordmanager.rst
new file mode 100644
index 0000000..e7ad716
--- /dev/null
+++ b/docs/connectionmanager/passwordmanager.rst
@@ -0,0 +1,35 @@
+================
+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
+------------------------------
+
+Unfortunatly at this time there is no way to change the master password.
+Neither is it possible to completely reset the password manager other then
+removing the `pglabuser.db` file.
+
+--------
+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.
diff --git a/docs/index.rst b/docs/index.rst
index ff59f1d..cf75cbd 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -7,11 +7,13 @@ pgLab User Manual
====================================================
.. toctree::
- :maxdepth: 2
+ :maxdepth: 3
:caption: Contents:
releasenotes
installation
+ connectionmanager/index
+ internals
Indices and tables
==================
diff --git a/docs/installation.rst b/docs/installation.rst
index 9a416a9..a7d69ac 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -8,9 +8,11 @@ Currently only binaries for Windows 64-bit are provided.
Windows
-------
-Downloads can be found `here `_.
+Downloads can be found `here
+`_.
-Unpack the contents of the 7zip archive to a folder of your choosing for instance
-`C:\\Program files\\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 `_.
+Unpack the contents of the 7zip archive to a folder of your choosing for
+instance `C:\\Program files\\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 `_.
diff --git a/docs/internals.rst b/docs/internals.rst
new file mode 100644
index 0000000..161e571
--- /dev/null
+++ b/docs/internals.rst
@@ -0,0 +1,11 @@
+=========
+Internals
+=========
+
+---------
+User data
+---------
+
+All connection configuration information is stored in
+`%AppData%\pglab\pglab\pglabuser.db` this is an sqlite database.
+
diff --git a/pglab/ConnectionController.cpp b/pglab/ConnectionController.cpp
index c3ae0c5..93b4d24 100644
--- a/pglab/ConnectionController.cpp
+++ b/pglab/ConnectionController.cpp
@@ -6,7 +6,9 @@
#include "DatabaseWindow.h"
#include "BackupDialog.h"
#include "PasswordPromptDialog.h"
+#include "ScopeGuard.h"
#include "ConnectionConfigurationWidget.h"
+#include
#include
#include
#include
@@ -156,33 +158,36 @@ std::shared_ptr 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::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::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)
@@ -224,29 +229,48 @@ bool ConnectionController::decodeConnectionPassword(QUuid id, QByteArray encoded
return res;
}
+void ConnectionController::resetPasswordManager()
+{
+ auto&& user_cfg_db = m_masterController->userConfigDatabase();
+ user_cfg_db.transaction();
+ try
+ {
+ m_passwordManager->resetMasterPassword(user_cfg_db);
+ m_connectionTreeModel->clearAllPasswords();
+ user_cfg_db.commit();
+ }
+ catch (...)
+ {
+ user_cfg_db.rollback();
+ throw;
+ }
+}
+
bool ConnectionController::UnlockPasswordManagerIfNeeded()
{
- auto&& user_cfg_db = m_masterController->userConfigDatabase();
- if (m_passwordManager->initialized(user_cfg_db)) {
- if (!m_passwordManager->locked())
- return true;
+ auto&& user_cfg_db = m_masterController->userConfigDatabase();
+ if (m_passwordManager->initialized(user_cfg_db))
+ {
+ if (!m_passwordManager->locked())
+ return true;
- while (true) {
- // ask user for passphrase
+ while (true)
+ {
PassphraseResult pp_result = PassphrasePrompt();
if (!pp_result.success)
- break; // leave this retry loop
+ break;
- // user gave OK, if succeeds return true otherwise loop a prompt for password again.
- if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase)) {
+ // user gave OK, if succeeds return true otherwise loop a prompt for password again.
+ if (m_passwordManager->openDatabase(user_cfg_db, pp_result.passphrase))
+ {
setRelockTimer(pp_result.rememberForMinutes);
- return true;
- }
- }
- }
- else {
+ return true;
+ }
+ }
+ }
+ else
InitializePasswordManager();
- }
+
return false;
}
diff --git a/pglab/ConnectionController.h b/pglab/ConnectionController.h
index 71930a5..b0ebc99 100644
--- a/pglab/ConnectionController.h
+++ b/pglab/ConnectionController.h
@@ -12,6 +12,7 @@ class ConnectionTreeModel;
class ConnectionManagerWindow;
class PasswordManager;
class QTimer;
+class QSqlDatabase;
class PassphraseResult {
@@ -58,6 +59,7 @@ public:
bool UnlockPasswordManagerIfNeeded();
bool decodeConnectionPassword(QUuid id, QByteArray encoded, QString &out_password);
+ void resetPasswordManager();
private:
MasterController *m_masterController;
ConnectionList *m_connectionList = nullptr;
@@ -71,7 +73,6 @@ private:
std::shared_ptr 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);
diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp
index 4cac885..ebec31d 100644
--- a/pglab/ConnectionListModel.cpp
+++ b/pglab/ConnectionListModel.cpp
@@ -351,7 +351,17 @@ void ConnectionTreeModel::save(const QString &group_name, const ConnectionConfig
void ConnectionTreeModel::save(const ConnectionConfig &cc)
{
- saveToDb(cc);
+ saveToDb(cc);
+}
+
+void ConnectionTreeModel::clearAllPasswords()
+{
+ for (auto group : m_groups)
+ for (auto cc : group->connections())
+ {
+ cc->setEncodedPassword({});
+ saveToDb(*cc);
+ }
}
std::tuple ConnectionTreeModel::findConfig(const QUuid uuid) const
diff --git a/pglab/ConnectionListModel.h b/pglab/ConnectionListModel.h
index 39f8764..6ea9919 100644
--- a/pglab/ConnectionListModel.h
+++ b/pglab/ConnectionListModel.h
@@ -59,6 +59,7 @@ public:
/** Save changed config, group is not allowed to change
*/
void save(const ConnectionConfig &cc);
+ void clearAllPasswords();
/// Create a new group in the DB and place in the tree
std::variant addGroup(const QString &group_name);
std::optional removeGroup(int row);
diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp
index 5377631..4411663 100644
--- a/pglab/ConnectionManagerWindow.cpp
+++ b/pglab/ConnectionManagerWindow.cpp
@@ -115,3 +115,15 @@ void ConnectionManagerWindow::on_actionManual_triggered()
QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
}
+
+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();
+}
+
diff --git a/pglab/ConnectionManagerWindow.h b/pglab/ConnectionManagerWindow.h
index 680915d..4c70882 100644
--- a/pglab/ConnectionManagerWindow.h
+++ b/pglab/ConnectionManagerWindow.h
@@ -43,6 +43,8 @@ private slots:
void on_actionManual_triggered();
+ void on_actionReset_password_manager_triggered();
+
private:
Ui::ConnectionManagerWindow *ui;
MasterController *m_masterController;
diff --git a/pglab/ConnectionManagerWindow.ui b/pglab/ConnectionManagerWindow.ui
index 2d52b88..6404966 100644
--- a/pglab/ConnectionManagerWindow.ui
+++ b/pglab/ConnectionManagerWindow.ui
@@ -33,11 +33,18 @@
Fi&le
-
-
+
+
+
@@ -181,6 +188,11 @@ QToolButton {
Manual
+
+
+ Reset password manager
+
+
diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h
index 5e5a9c4..ff8c1ff 100644
--- a/pglablib/ConnectionConfig.h
+++ b/pglablib/ConnectionConfig.h
@@ -45,6 +45,7 @@ public:
using Connections = QVector>;
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.
diff --git a/releasenotes/config.yaml b/releasenotes/config.yaml
index fa9f8f3..54fcadc 100644
--- a/releasenotes/config.yaml
+++ b/releasenotes/config.yaml
@@ -1,2 +1,10 @@
---
+sections:
+ # The prelude section is implicitly included.
+ - [features, New Features]
+ - [other, Changes]
+ - [issues, Known Issues]
+ - [upgrade, Upgrade Notes]
+ - [security, Security Issues]
+ - [fixes, Bug Fixes]
encoding: utf8
diff --git a/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml b/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
new file mode 100644
index 0000000..08ca7f4
--- /dev/null
+++ b/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - |
+ Added menu option to reset the password managers master password.
+ This will ofcourse also clear all saved passwords.
From 3f41fc07c3b0aa701e4a2ddb7d1d7a1a07331ca2 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 07:45:59 +0200
Subject: [PATCH 14/64] Was not saved properly
---
docs/connectionmanager/passwordmanager.rst | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/docs/connectionmanager/passwordmanager.rst b/docs/connectionmanager/passwordmanager.rst
index e7ad716..f2e11f8 100644
--- a/docs/connectionmanager/passwordmanager.rst
+++ b/docs/connectionmanager/passwordmanager.rst
@@ -18,9 +18,8 @@ prompt the program will prompt you for the password of the database connection.
Resetting the password manager
------------------------------
-Unfortunatly at this time there is no way to change the master password.
-Neither is it possible to completely reset the password manager other then
-removing the `pglabuser.db` file.
+If you loose your password and want to start with a new empy password manager
+use the menu option File > Reset password manager.
--------
Security
From df24564d6bd38a32ee1023e62c150572ecfbd3b7 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 08:02:00 +0200
Subject: [PATCH 15/64] fix save connection password on uninitialized manager
did not save
it did initialize the manager
---
pglab/ConnectionController.cpp | 5 ++++-
...-password-on-uninitialized-manager-591761764efbabb8.yaml | 6 ++++++
2 files changed, 10 insertions(+), 1 deletion(-)
create mode 100644 releasenotes/notes/fix-save-password-on-uninitialized-manager-591761764efbabb8.yaml
diff --git a/pglab/ConnectionController.cpp b/pglab/ConnectionController.cpp
index 93b4d24..0f00e9b 100644
--- a/pglab/ConnectionController.cpp
+++ b/pglab/ConnectionController.cpp
@@ -252,7 +252,7 @@ bool ConnectionController::UnlockPasswordManagerIfNeeded()
if (m_passwordManager->initialized(user_cfg_db))
{
if (!m_passwordManager->locked())
- return true;
+ return true;
while (true)
{
@@ -269,7 +269,10 @@ bool ConnectionController::UnlockPasswordManagerIfNeeded()
}
}
else
+ {
InitializePasswordManager();
+ return true;
+ }
return false;
}
diff --git a/releasenotes/notes/fix-save-password-on-uninitialized-manager-591761764efbabb8.yaml b/releasenotes/notes/fix-save-password-on-uninitialized-manager-591761764efbabb8.yaml
new file mode 100644
index 0000000..db6b4b3
--- /dev/null
+++ b/releasenotes/notes/fix-save-password-on-uninitialized-manager-591761764efbabb8.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ Fix that when saving the password of a connection to an uninitialized password
+ manager the program did not save the password. It would correctly initialize
+ the password manager and saving worked the second time.
From 38290145c89256b58700041ad3580be9708971c1 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 08:14:01 +0200
Subject: [PATCH 16/64] switch documentation to furo theme
Because cleaner look, more useful contents sidebar and light/dark mode support.
---
docs/conf.py | 4 +---
docs/requirements.txt | 2 +-
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
index 6ce7a1e..ae3d2cb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -48,9 +48,7 @@ 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.
#
-import sphinx_theme_pd
-html_theme = 'sphinx_theme_pd'
-html_theme_path = [sphinx_theme_pd.get_html_theme_path()]
+html_theme = 'furo'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 2fc0c18..8ef2918 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,4 @@
reno[sphinx]==3.5.0
Sphinx==5.1.1
-sphinx-theme-pd==0.8.3
+furo==0.8.3
From eac308c5b1ca6177cc6e53747dbd6196920dc2d2 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 08:14:18 +0200
Subject: [PATCH 17/64] Fix backslashes need escaping.
---
docs/internals.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/internals.rst b/docs/internals.rst
index 161e571..697234b 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -7,5 +7,5 @@ User data
---------
All connection configuration information is stored in
-`%AppData%\pglab\pglab\pglabuser.db` this is an sqlite database.
+`%AppData%\\pglab\\pglab\\pglabuser.db` this is an sqlite database.
From 90d9c1109d8d83848a7d74669a716e8c3301cf37 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 08:25:58 +0200
Subject: [PATCH 18/64] Cleanup old files
---
pglabAll.pro.user.4.10-pre1 | 481 ------------------------------------
src/CMakeLists.txt | 80 ------
2 files changed, 561 deletions(-)
delete mode 100644 pglabAll.pro.user.4.10-pre1
delete mode 100644 src/CMakeLists.txt
diff --git a/pglabAll.pro.user.4.10-pre1 b/pglabAll.pro.user.4.10-pre1
deleted file mode 100644
index 319c1af..0000000
--- a/pglabAll.pro.user.4.10-pre1
+++ /dev/null
@@ -1,481 +0,0 @@
-
-
-
-
-
- EnvironmentId
- {78888978-f53a-469c-ac73-a847c4fa88dd}
-
-
- ProjectExplorer.Project.ActiveTarget
- 0
-
-
- ProjectExplorer.Project.EditorSettings
-
- true
- false
- true
-
- Cpp
-
- CppGlobal
-
-
-
- QmlJS
-
- QmlJSGlobal
-
-
- 2
- UTF-8
- false
- 4
- false
- 80
- true
- true
- 1
- true
- false
- 2
- true
- true
- 0
- 8
- true
- 0
- true
- true
- true
- false
-
-
-
- ProjectExplorer.Project.PluginSettings
-
-
- -fno-delayed-template-parsing
-
- true
-
- false
- Builtin.TidyAndClazy
-
-
- X:/Prog/pglab/pglab/ConnectionManagerWindow.cpp
- X:/Prog/pglab/pglab/ConnectionConfigurationWidget.cpp
- X:/Prog/pglab/pglab/ConnectionController.cpp
- X:/Prog/pglab/pglab/ConnectionListModel.cpp
-
-
- true
-
-
-
- ProjectExplorer.Project.Target.0
-
- Desktop Qt 5.13.0 MSVC2017 32bit
- Desktop Qt 5.13.0 MSVC2017 32bit
- qt.qt5.5130.win32_msvc2017_kit
- 0
- 0
- 0
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- false
- false
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
- false
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
- false
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Debug
- Debug
- Qt4ProjectManager.Qt4BuildConfiguration
- 2
- true
-
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Release
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- false
-
- false
- false
- true
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
- false
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
- false
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Release
- Release
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Profile
-
-
- true
- qmake
-
- QtProjectManager.QMakeBuildStep
- true
-
- false
- true
- true
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- false
-
-
- false
-
- 2
- Build
-
- ProjectExplorer.BuildSteps.Build
-
-
-
- true
- Make
-
- Qt4ProjectManager.MakeStep
-
- true
- clean
-
- false
-
- 1
- Clean
-
- ProjectExplorer.BuildSteps.Clean
-
- 2
- false
-
- Profile
- Profile
- Qt4ProjectManager.Qt4BuildConfiguration
- 0
- true
-
- 3
-
-
- 0
- Deploy
-
- ProjectExplorer.BuildSteps.Deploy
-
- 1
- Deploy Configuration
-
- ProjectExplorer.DefaultDeployConfiguration
-
- 1
-
-
- dwarf
-
- cpu-cycles
-
-
- 250
- -F
- true
- 4096
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- kcachegrind
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
- pglab
-
- Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/pglab/pglab.pro
-
- 3768
- false
- true
- true
- false
- false
- true
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/pglab
-
-
- dwarf
-
- cpu-cycles
-
-
- 250
- -F
- true
- 4096
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- kcachegrind
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
- pglabtests
-
- Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/tests/pglabtests/pglabtests.pro
-
- 3768
- false
- true
- true
- false
- false
- true
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/tests/pglabtests
-
-
- dwarf
-
- cpu-cycles
-
-
- 250
- -F
- true
- 4096
- false
- false
- 1000
-
- true
-
- false
- false
- false
- false
- true
- 0.01
- 10
- true
- kcachegrind
- 1
- 25
-
- 1
- true
- false
- true
- valgrind
-
- 0
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
-
- 2
-
- PgsqlTests
-
- Qt4ProjectManager.Qt4RunConfiguration:X:/Prog/pglab/tests/PgsqlTests/PgsqlTests.pro
-
- 3768
- false
- true
- true
- false
- false
- true
-
- X:/Prog/build-pglabAll-Desktop_Qt_5_13_0_MSVC2017_32bit-Debug/tests/PgsqlTests
-
- 3
-
-
-
- ProjectExplorer.Project.TargetCount
- 1
-
-
- ProjectExplorer.Project.Updater.FileVersion
- 21
-
-
- Version
- 21
-
-
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
deleted file mode 100644
index 6ef11ab..0000000
--- a/src/CMakeLists.txt
+++ /dev/null
@@ -1,80 +0,0 @@
-cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
-project(pglaball VERSION 0.1 LANGUAGES CXX)
-
-set (CMAKE_PREFIX_PATH /usr/lib/x86_64-linux-gnu/qt5/mkspecs/features/data/cmake)
-set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
-
-# Must use GNUInstallDirs to install libraries into correct
-# locations on all platforms.
-include(GNUInstallDirs)
-
-include(CheckCXXCompilerFlag)
-
-function(enable_cxx_compiler_flag_if_supported flag)
- string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" flag_already_set)
- if(flag_already_set EQUAL -1)
- check_cxx_compiler_flag("${flag}" flag_supported)
- if(flag_supported)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
- endif()
- unset(flag_supported CACHE)
- endif()
-endfunction()
-
-
-enable_cxx_compiler_flag_if_supported("-Wall")
-
-set(CMAKE_CXX_EXTENSIONS OFF)
-# set(CMAKE_CXX_STANDARD 14)
-# set(CMAKE_CXX_STANDARD_REQUIRED ON)
-
-# Find includes in corresponding build directories
-set(CMAKE_INCLUDE_CURRENT_DIR ON)
-# Instruct CMake to run moc automatically when needed.
-
-find_package(Botan REQUIRED)
-add_library(botan UNKNOWN IMPORTED)
-set_property(TARGET botan PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BOTAN_INCLUDE_DIRS})
-set_property(TARGET botan PROPERTY IMPORTED_LOCATION ${BOTAN_LIBRARIES})
-
-find_package(PostgreSQL REQUIRED)
-add_library(postgresql UNKNOWN IMPORTED)
-set_property(TARGET postgresql PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PostgreSQL_INCLUDE_DIRS})
-set_property(TARGET postgresql PROPERTY IMPORTED_LOCATION ${PostgreSQL_LIBRARIES})
-
-find_package(Qt5Widgets 5.7 REQUIRED)
-add_library(Qt5Widgets INTERFACE IMPORTED)
-set_property(TARGET Qt5Widgets PROPERTY
- INTERFACE_INCLUDE_DIRECTORIES ${Qt5Widgets_INCLUDE_DIRS})
-
-find_package(Fmt 4.0 REQUIRED)
-add_library(fmt INTERFACE IMPORTED)
-set_property(TARGET fmt PROPERTY
- INTERFACE_INCLUDE_DIRECTORIES ${fmt_INCLUDE_DIRS})
-
-find_package(Boost 1.63 COMPONENTS system REQUIRED )
-add_library(boost INTERFACE IMPORTED)
-set_property(TARGET boost PROPERTY
- INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIR})
-
-add_library(boost-system SHARED IMPORTED)
-set_property(TARGET boost-system PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${Boost_INCLUDE_DIRS})
-set_property(TARGET boost-system PROPERTY IMPORTED_LOCATION ${Boost_SYSTEM_LIBRARY})
-
-find_package(GTest REQUIRED)
-add_library(gtest UNKNOWN IMPORTED)
-set_property(TARGET gtest PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS})
-set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${GTEST_LIBRARIES})
-
-find_package(Threads)
-
-add_definitions(-DQT_USE_QSTRINGBUILDER)
-
-enable_testing()
-
-add_subdirectory(core)
-add_subdirectory(pgsql)
-add_subdirectory(pglab)
-
-
-
From 677302b5a72bd9339c74ea4022ec29c5827988e7 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 13:24:39 +0200
Subject: [PATCH 19/64] Bind F5 to curd and catalog refresh actions
---
pglab/DatabaseWindow.ui | 6 ++++++
releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml | 4 ++++
2 files changed, 10 insertions(+)
create mode 100644 releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
diff --git a/pglab/DatabaseWindow.ui b/pglab/DatabaseWindow.ui
index 2560e24..c2e24d2 100644
--- a/pglab/DatabaseWindow.ui
+++ b/pglab/DatabaseWindow.ui
@@ -298,11 +298,17 @@
Refresh
+
+ F5
+
Refresh
+
+ F5
+
diff --git a/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml b/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
new file mode 100644
index 0000000..dda4c52
--- /dev/null
+++ b/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
@@ -0,0 +1,4 @@
+---
+fixes:
+ - |
+ Make F5 work again for refreshing the catalog and CRUD views.
\ No newline at end of file
From da19c46d5eba53a1d867f80923cb95cfae4c0e35 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 14:33:51 +0200
Subject: [PATCH 20/64] Improve editing of connection password
Previously only a new password was saved if the save password checkbox was checked, Which always
started in the unchecked state. Now when editing existing connection the save password checkbox now
reflects if a password has been saved. Only when the password field is edited the program will update
the saved password. If the save password checkbox is unchecked then clear the save password.
---
pglab/ConnectionConfigurationWidget.cpp | 20 ++++++++++++++++++-
pglab/ConnectionConfigurationWidget.h | 4 ++++
pglab/ConnectionController.cpp | 5 +++--
pglab/ConnectionListModel.cpp | 6 +++++-
pglablib/ConnectionConfig.cpp | 2 +-
pglablib/ConnectionConfig.h | 2 +-
...oved-password-saving-cc6b3d633d1e7c3b.yaml | 6 ++++++
7 files changed, 39 insertions(+), 6 deletions(-)
create mode 100644 releasenotes/notes/improved-password-saving-cc6b3d633d1e7c3b.yaml
diff --git a/pglab/ConnectionConfigurationWidget.cpp b/pglab/ConnectionConfigurationWidget.cpp
index 34dc744..8690359 100644
--- a/pglab/ConnectionConfigurationWidget.cpp
+++ b/pglab/ConnectionConfigurationWidget.cpp
@@ -102,6 +102,7 @@ ConnectionConfigurationWidget::ConnectionConfigurationWidget(
edtPassword = new QLineEdit;
SET_OBJECT_NAME(edtPassword);
edtPassword->setEchoMode(QLineEdit::PasswordEchoOnEdit);
+ connect(edtPassword, &QLineEdit::textEdited, this, &ConnectionConfigurationWidget::passwordEdited);
cbSavePassword = new QCheckBox;
SET_OBJECT_NAME(cbSavePassword);
@@ -212,6 +213,13 @@ void ConnectionConfigurationWidget::setData(const ConnectionConfig &cfg)
edtCrl->setText(cfg.sslCrl());
encodedPassword = cfg.encodedPassword();
+ cbSavePassword->setCheckState(
+ encodedPassword.isEmpty()
+ ? Qt::CheckState::Unchecked
+ : Qt::CheckState::Checked
+ );
+
+ passwordChanged = false;
}
ConnectionConfig ConnectionConfigurationWidget::data() const
@@ -240,7 +248,12 @@ QString ConnectionConfigurationWidget::group() const
bool ConnectionConfigurationWidget::savePassword() const
{
- return cbSavePassword->isChecked() && !edtPassword->text().isEmpty();
+ return cbSavePassword->isChecked() && !edtPassword->text().isEmpty() && passwordChanged;
+}
+
+bool ConnectionConfigurationWidget::clearPassword() const
+{
+ return !cbSavePassword->isChecked();
}
void ConnectionConfigurationWidget::testConnection()
@@ -278,3 +291,8 @@ void ConnectionConfigurationWidget::handleTestResult(TestConnectionResult result
cmbDbname->setCurrentText(current);
}
+void ConnectionConfigurationWidget::passwordEdited(const QString &)
+{
+ passwordChanged = true;
+}
+
diff --git a/pglab/ConnectionConfigurationWidget.h b/pglab/ConnectionConfigurationWidget.h
index a67fbf4..2f38102 100644
--- a/pglab/ConnectionConfigurationWidget.h
+++ b/pglab/ConnectionConfigurationWidget.h
@@ -34,7 +34,9 @@ public:
void setData(const ConnectionConfig &cfg);
ConnectionConfig data() const;
QString group() const;
+
bool savePassword() const;
+ bool clearPassword() const;
public slots:
void testConnection();
@@ -45,6 +47,7 @@ private:
QUuid m_uuid;
QByteArray encodedPassword;
+ bool passwordChanged;
QLabel *lblGroup;
QComboBox *cmbbxGroup;
@@ -77,6 +80,7 @@ private:
QFormLayout *formLayout;
void handleTestResult(TestConnectionResult result);
+ void passwordEdited(const QString &);
};
#endif // CONNECTIONCONFIGURATIONWIDGET_H
diff --git a/pglab/ConnectionController.cpp b/pglab/ConnectionController.cpp
index 0f00e9b..f8815dd 100644
--- a/pglab/ConnectionController.cpp
+++ b/pglab/ConnectionController.cpp
@@ -117,9 +117,10 @@ void ConnectionController::saveConnection(ConnectionConfigurationWidget &w)
{
auto cc = w.data();
auto grp = w.group();
- if (w.savePassword()) {
+ if (w.savePassword())
encryptPassword(cc);
- }
+ if (w.clearPassword())
+ cc.setEncodedPassword({});
m_connectionTreeModel->save(grp, cc);
}
diff --git a/pglab/ConnectionListModel.cpp b/pglab/ConnectionListModel.cpp
index ebec31d..1b8c892 100644
--- a/pglab/ConnectionListModel.cpp
+++ b/pglab/ConnectionListModel.cpp
@@ -82,7 +82,11 @@ R"__(INSERT OR REPLACE INTO connection
q.bindValue(":sslkey", cc.sslKey());
q.bindValue(":sslrootcert", cc.sslRootCert());
q.bindValue(":sslcrl", cc.sslCrl());
- q.bindValue(":password", cc.encodedPassword());
+ auto& encodedPassword = cc.encodedPassword();
+ if (encodedPassword.isEmpty())
+ q.bindValue(":password", QVariant());
+ else
+ q.bindValue(":password", encodedPassword);
if (!q.exec()) {
auto sql_error = q.lastError();
diff --git a/pglablib/ConnectionConfig.cpp b/pglablib/ConnectionConfig.cpp
index 7356108..f324c24 100644
--- a/pglablib/ConnectionConfig.cpp
+++ b/pglablib/ConnectionConfig.cpp
@@ -272,7 +272,7 @@ QString ConnectionConfig::makeLongDescription() const
return result;
}
-QByteArray ConnectionConfig::encodedPassword() const
+const QByteArray& ConnectionConfig::encodedPassword() const
{
return m_encodedPassword;
}
diff --git a/pglablib/ConnectionConfig.h b/pglablib/ConnectionConfig.h
index ff8c1ff..390202a 100644
--- a/pglablib/ConnectionConfig.h
+++ b/pglablib/ConnectionConfig.h
@@ -119,7 +119,7 @@ public:
bool operator==(QUuid id) const { return m_uuid == id; }
QString makeLongDescription() const;
- QByteArray encodedPassword() const;
+ const QByteArray& encodedPassword() const;
void setEncodedPassword(const QByteArray &encodedPassword);
// void write(QDataStream &out) const;
diff --git a/releasenotes/notes/improved-password-saving-cc6b3d633d1e7c3b.yaml b/releasenotes/notes/improved-password-saving-cc6b3d633d1e7c3b.yaml
new file mode 100644
index 0000000..16de39e
--- /dev/null
+++ b/releasenotes/notes/improved-password-saving-cc6b3d633d1e7c3b.yaml
@@ -0,0 +1,6 @@
+---
+other:
+ - |
+ The way in which editing the password of an existing connection works has been changed to
+ be consistent with creating a new connection and to allow for clearing the saved password
+ by unselecting save password.
\ No newline at end of file
From 487f84cf95cb3b3cc9a28307767b07d5b4eeb5da Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 14:52:24 +0200
Subject: [PATCH 21/64] Update docs and improve releasenotes
---
docs/connectionmanager/editingconnection.rst | 35 +++++++++++++++++++
docs/connectionmanager/index.rst | 1 +
docs/connectionmanager/passwordmanager.rst | 2 +-
.../notes/f5-bindings-5acb4d8b18626cef.yaml | 2 +-
4 files changed, 38 insertions(+), 2 deletions(-)
create mode 100644 docs/connectionmanager/editingconnection.rst
diff --git a/docs/connectionmanager/editingconnection.rst b/docs/connectionmanager/editingconnection.rst
new file mode 100644
index 0000000..742b944
--- /dev/null
+++ b/docs/connectionmanager/editingconnection.rst
@@ -0,0 +1,35 @@
+===================
+Editing connections
+===================
+
+--------------
+New connection
+--------------
+
+With the :guilabel:`Add connection` button you can create a new connection
+configuration.
+
+Entering a password is optional at this stage. You may need it ofcourse if you
+want to save the password or test the connection. When you :guilabel:`test` the
+connection and the the connection works a list of databases is populated to
+select from for the database field.
+
+-----------------
+Edit a connection
+-----------------
+
+To edit a configuration you need to click the :guilabel:`Configure connection`.
+
+Note the password field will always be empty even if a password was saved. As
+long as you do not edit this field the saved password will stay unchanged. As
+soon as you change this field or when you clear the :guilabel:`Save password`
+checkbox the password will be updated or cleared when you accept your changes
+by clicking :guilabel:`OK`.
+
+-----------
+Edit a copy
+-----------
+
+When you select :guilabel:`Configure copy` you will basically start editing
+a new connection with the details of the connection that was selected. Note
+the password will not be copied.
diff --git a/docs/connectionmanager/index.rst b/docs/connectionmanager/index.rst
index 909e32f..1ceb7aa 100644
--- a/docs/connectionmanager/index.rst
+++ b/docs/connectionmanager/index.rst
@@ -4,4 +4,5 @@ Connection Manager
.. toctree::
+ editingconnection
passwordmanager
diff --git a/docs/connectionmanager/passwordmanager.rst b/docs/connectionmanager/passwordmanager.rst
index f2e11f8..7f04e56 100644
--- a/docs/connectionmanager/passwordmanager.rst
+++ b/docs/connectionmanager/passwordmanager.rst
@@ -19,7 +19,7 @@ Resetting the password manager
------------------------------
If you loose your password and want to start with a new empy password manager
-use the menu option File > Reset password manager.
+use the menu option :menuselection:`File --> Reset password manager`.
--------
Security
diff --git a/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml b/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
index dda4c52..14b2410 100644
--- a/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
+++ b/releasenotes/notes/f5-bindings-5acb4d8b18626cef.yaml
@@ -1,4 +1,4 @@
---
fixes:
- |
- Make F5 work again for refreshing the catalog and CRUD views.
\ No newline at end of file
+ Make :kbd:`F5` work again for refreshing the catalog and CRUD views.
From 0483dc8b3dd0fac12769a5ce783f54a50a989144 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 15:01:33 +0200
Subject: [PATCH 22/64] Improved releasenotes manual can also be opened from a
databasewindow now.
---
pglab/About.cpp | 7 +++++++
pglab/About.h | 1 +
pglab/ConnectionManagerWindow.cpp | 3 +--
pglab/DatabaseWindow.cpp | 6 ++++++
pglab/DatabaseWindow.h | 2 ++
pglab/DatabaseWindow.ui | 6 ++++++
releasenotes/notes/about-7eeb6e282f8e2ef1.yaml | 2 +-
releasenotes/notes/help-usermanual-1ff757b8969fc8e9.yaml | 4 ++++
.../notes/reset-master-password-6a8f5ccf5a052344.yaml | 5 +++--
9 files changed, 31 insertions(+), 5 deletions(-)
create mode 100644 releasenotes/notes/help-usermanual-1ff757b8969fc8e9.yaml
diff --git a/pglab/About.cpp b/pglab/About.cpp
index a38b4b1..75c1b84 100644
--- a/pglab/About.cpp
+++ b/pglab/About.cpp
@@ -1,7 +1,9 @@
#include "About.h"
#include
+#include
#include
#include
+#include
void ShowAboutDialog(QWidget *parent)
{
@@ -21,3 +23,8 @@ void ShowAboutDialog(QWidget *parent)
"attribution 3.0 license.
"
).arg(year).arg(releaseNotesUrl).arg(pgLabVersionString));
}
+
+void OpenManual()
+{
+ QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
+}
diff --git a/pglab/About.h b/pglab/About.h
index 1274f37..4311556 100644
--- a/pglab/About.h
+++ b/pglab/About.h
@@ -3,3 +3,4 @@
#include
void ShowAboutDialog(QWidget *parent);
+void OpenManual();
diff --git a/pglab/ConnectionManagerWindow.cpp b/pglab/ConnectionManagerWindow.cpp
index 4411663..68a1a49 100644
--- a/pglab/ConnectionManagerWindow.cpp
+++ b/pglab/ConnectionManagerWindow.cpp
@@ -4,7 +4,6 @@
#include "MasterController.h"
#include "ConnectionController.h"
#include "ConnectionListModel.h"
-#include
#include
#include
#include
@@ -112,7 +111,7 @@ void ConnectionManagerWindow::on_actionAbout_triggered()
void ConnectionManagerWindow::on_actionManual_triggered()
{
- QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
+ OpenManual();
}
diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp
index 57908f6..c003c86 100644
--- a/pglab/DatabaseWindow.cpp
+++ b/pglab/DatabaseWindow.cpp
@@ -442,3 +442,9 @@ void DatabaseWindow::on_actionShow_connection_manager_triggered()
{
m_masterController->connectionController()->showConnectionManager();
}
+
+void DatabaseWindow::on_actionManual_triggered()
+{
+ OpenManual();
+}
+
diff --git a/pglab/DatabaseWindow.h b/pglab/DatabaseWindow.h
index 9a8a8f6..ab259ae 100644
--- a/pglab/DatabaseWindow.h
+++ b/pglab/DatabaseWindow.h
@@ -106,6 +106,8 @@ private slots:
void on_actionSave_copy_of_query_as_triggered();
void on_actionShow_connection_manager_triggered();
+ void on_actionManual_triggered();
+
public:
virtual void setTitleForWidget(QWidget *widget, QString title, QString hint) override;
virtual void setIconForWidget(QWidget *widget, QIcon icon) override;
diff --git a/pglab/DatabaseWindow.ui b/pglab/DatabaseWindow.ui
index c2e24d2..8000735 100644
--- a/pglab/DatabaseWindow.ui
+++ b/pglab/DatabaseWindow.ui
@@ -94,6 +94,7 @@
Help
+
@@ -325,6 +326,11 @@
Show connection manager
+
+
+ Manual
+
+
diff --git a/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
index 33e7627..23ca431 100644
--- a/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
+++ b/releasenotes/notes/about-7eeb6e282f8e2ef1.yaml
@@ -1,6 +1,6 @@
---
other:
- - The connection window now has an About item in the file menu.
+ - The connection window now has an :menuselection:`Help --> About` menuitem.
- The about dialog now uses html for enhanced formatting and hyperlinks.
- |
The about dialog now declares the usage of Icons8 icons and provides a
diff --git a/releasenotes/notes/help-usermanual-1ff757b8969fc8e9.yaml b/releasenotes/notes/help-usermanual-1ff757b8969fc8e9.yaml
new file mode 100644
index 0000000..cf86242
--- /dev/null
+++ b/releasenotes/notes/help-usermanual-1ff757b8969fc8e9.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ - |
+ The user manual can now be accessed from the menu :menuselection:`Help --> Manual`.
diff --git a/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml b/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
index 08ca7f4..427738b 100644
--- a/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
+++ b/releasenotes/notes/reset-master-password-6a8f5ccf5a052344.yaml
@@ -1,5 +1,6 @@
---
features:
- |
- Added menu option to reset the password managers master password.
- This will ofcourse also clear all saved passwords.
+ Added :menuselection:`File --> Reset password manager` menu option to reset
+ the password managers master password. This will ofcourse also clear all
+ saved passwords.
From 21dd9fd930f15c338e2ad8b30b169013bb62d7f4 Mon Sep 17 00:00:00 2001
From: eelke
Date: Mon, 5 Sep 2022 15:41:02 +0200
Subject: [PATCH 23/64] Fix furo version
---
docs/requirements.txt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 8ef2918..729395f 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,3 @@
reno[sphinx]==3.5.0
Sphinx==5.1.1
-furo==0.8.3
-
+furo==2022.6.21
From 5b85efd8dfc5857c1b6c647c4d44f0b4865de7af Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Mon, 5 Sep 2022 18:08:45 +0000
Subject: [PATCH 24/64] Deleted readme.md
---
readme.md | 0
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 readme.md
diff --git a/readme.md b/readme.md
deleted file mode 100644
index e69de29..0000000
From 54e39ccdb396789f38e3e06fc7bf6d5740adec59 Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Mon, 5 Sep 2022 18:14:58 +0000
Subject: [PATCH 25/64] Add README.md
---
README.md | 14 ++++++++++++++
1 file changed, 14 insertions(+)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3e38338
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+The program is build with Qt 6.3 using QtCreator and the Visual Studio 2019 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
+- fmt
+- googletest
+- libpq
+
From 9277731c4e82105995a5ad329293cc0db1f1b020 Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Tue, 6 Sep 2022 11:17:18 +0000
Subject: [PATCH 26/64] Resolve "Improve GENERATED support"
---
pglab/catalog/models/ColumnTableModel.cpp | 46 +-
pglab/catalog/models/ColumnTableModel.h | 3 +-
pglab/crud/CrudModel.cpp | 48 +-
pglab/crud/CrudModel.h | 4 +
pglablib/catalog/PgAttribute.cpp | 56 +-
pglablib/catalog/PgAttribute.h | 14 +
pglablib/catalog/PgAttributeContainer.cpp | 6 +-
pglablib/catalog/PgContainer.h | 30 ++
pglablib/catalog/PgKeywordList.cpp | 205 +-------
pglablib/catalog/PgSequence.cpp | 57 ++-
pglablib/catalog/PgSequence.h | 27 +-
pglablib/catalog/kwlist.h | 484 ++++++++++++++++++
.../generated-support-1528dad543a7525f.yaml | 15 +
13 files changed, 749 insertions(+), 246 deletions(-)
create mode 100644 pglablib/catalog/kwlist.h
create mode 100644 releasenotes/notes/generated-support-1528dad543a7525f.yaml
diff --git a/pglab/catalog/models/ColumnTableModel.cpp b/pglab/catalog/models/ColumnTableModel.cpp
index 0e9de61..0ff9b47 100644
--- a/pglab/catalog/models/ColumnTableModel.cpp
+++ b/pglab/catalog/models/ColumnTableModel.cpp
@@ -12,6 +12,7 @@
#include "ScopeGuard.h"
#include "SqlFormattingUtils.h"
#include
+#include
namespace {
@@ -142,18 +143,6 @@ int ColumnTableModel::columnCount(const QModelIndex &/*parent*/) const
Oid ColumnTableModel::getType(int /*column*/) const
{
Oid oid = Pgsql::varchar_oid;
-// switch (column) {
-// case TypeCol:
-// case NameCol:
-// case NullCol:
-// case DefaultCol:
-// case CollationCol:
-// oid = VARCHAROID;
-// break;
-
-// c = tr("Collation");
-// break;
-// }
return oid;
}
@@ -191,7 +180,7 @@ QVariant ColumnTableModel::getData(const QModelIndex &index) const
v = QString::fromStdU16String(t.notnull ? u"" : u"N");
break;
case DefaultCol:
- v = t.defaultValue;
+ v = getDefaultString(t);
break;
case ForeignKeyCol:
v = getFKey(t);
@@ -222,7 +211,36 @@ QString ColumnTableModel::getFKey(const PgAttribute &column) const
result = getForeignKeyConstraintReferencesShort(*m_catalog, elem);
}
}
- return result;
+ return result;
+}
+
+QString ColumnTableModel::getDefaultString(const PgAttribute &column) const
+{
+ if (column.identity != '\0') {
+ QString sql = "GENERATED ";
+ switch (column.identity) {
+ case 'a':
+ sql += "ALWAYS";
+ break;
+ case 'd':
+ sql += "BY DEFAULT";
+ break;
+ }
+ sql += " AS IDENTITY";
+ // TODO sequence options might be missing
+ return sql;
+ }
+
+ if (column.generated != '\0')
+ {
+ return "GENERATED ALWAYS AS " % column.defaultValue % " STORED";
+ }
+
+
+ if (column.hasdef)
+ return column.defaultValue;
+
+ return {};
}
void ColumnTableModel::refresh()
diff --git a/pglab/catalog/models/ColumnTableModel.h b/pglab/catalog/models/ColumnTableModel.h
index 8a70b77..edee3fd 100644
--- a/pglab/catalog/models/ColumnTableModel.h
+++ b/pglab/catalog/models/ColumnTableModel.h
@@ -55,8 +55,7 @@ protected:
QMetaObject::Connection refreshConnection;
QString getFKey(const PgAttribute &column) const;
-
-
+ QString getDefaultString(const PgAttribute &column) const;
private slots:
/// Retrieves required data from catalog, called everytime it might have changed
diff --git a/pglab/crud/CrudModel.cpp b/pglab/crud/CrudModel.cpp
index 253b9ba..a049223 100644
--- a/pglab/crud/CrudModel.cpp
+++ b/pglab/crud/CrudModel.cpp
@@ -169,7 +169,7 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const
if (index.column() < PreColumnCount)
{
if (role == Qt::DisplayRole || role == CustomSortRole)
- return index.row();
+ return index.row() + 1;
else if (role == CustomDataTypeRole)
return Pgsql::int4_oid;
@@ -231,6 +231,7 @@ void CrudModel::loadIntoModel(std::shared_ptr data)
beginResetModel();
m_pendingRowList.clear();
m_roData = data;
+ initializeColumnList();
lastRowKey = data->rows() - 1;
initRowMapping();
appendNewRow();
@@ -276,7 +277,7 @@ Qt::ItemFlags CrudModel::flags(const QModelIndex &index) const
if (index.column() < PreColumnCount)
return flags;
- if (m_primaryKey)
+ if (m_primaryKey && !columnIsReadOnly(index.column()))
flags |= Qt::ItemIsEditable;
return flags;
@@ -419,21 +420,16 @@ std::tuple CrudModel::createInsertQuery(const PendingRow
QTextStream q(&buffer);
q << "INSERT INTO " << table_name << "(";
- auto columns = m_database->catalog()->attributes()->getColumnsForRelation(m_table->oid());
bool first = true;
for (const auto& e : data)
{
- int num = e.first + 1;
- auto find_res = std::find_if(columns.begin(), columns.end(),
- [num] (const auto &elem) -> bool { return num == elem.num; });
- if (find_res != columns.end())
- {
- if (first)
- first = false;
- else
- q << ",";
- q << find_res->name;
- }
+ int num = e.first;
+ auto&& column = columnList[num];
+ if (first)
+ first = false;
+ else
+ q << ",";
+ q << column.name;
}
q << ") VALUES ($1";
for (size_t p = 2; p <= data.size(); ++p)
@@ -515,6 +511,30 @@ bool CrudModel::IsLastRow(RowMappingVector::iterator mapping_iter) const
return mapping_iter == --m_rowMapping.end();
}
+bool CrudModel::columnIsReadOnly(int column_index) const
+{
+ if (m_roData == nullptr)
+ return true;
+
+ auto&& column = columnList[column_index - PreColumnCount];
+ return column.getIdentity() == PgAttribute::Identity::Always
+ || column.getGenerated() != PgAttribute::Generated::None;
+}
+
+void CrudModel::initializeColumnList()
+{
+ columnList.clear();
+ columnList.reserve(m_roData->cols());
+ auto columns = m_database->catalog()->attributes()->getColumnsForRelation(m_table->oid());
+ for (int col = 0; col < m_roData->cols(); ++col)
+ {
+ int attnum = m_roData->ftableCol(col);
+ auto find_result = std::find_if(columns.begin(), columns.end(), [attnum](const PgAttribute &att) { return att.num == attnum; });
+ assert(find_result != columns.end());
+ columnList.push_back(*find_result);
+ }
+}
+
bool CrudModel::submit()
{
return savePendingChanges();
diff --git a/pglab/crud/CrudModel.h b/pglab/crud/CrudModel.h
index e852832..4cf7f77 100644
--- a/pglab/crud/CrudModel.h
+++ b/pglab/crud/CrudModel.h
@@ -15,6 +15,7 @@
#include
#include
+class PgAttribute;
class PgConstraint;
class OpenDatabase;
@@ -210,6 +211,7 @@ private:
bool callLoadData = false;
std::shared_ptr m_roData;
+ std::vector columnList; // list of columnMeta 1 to 1 with columns in m_roData.
PendingRowList m_pendingRowList;
@@ -256,6 +258,8 @@ private:
void RemoveRangeOfRowsFromModel(IntegerRange row_range);
bool IsLastRow(RowMappingVector::iterator mapping_iter) const;
+ bool columnIsReadOnly(int column_index) const;
+ void initializeColumnList();
private slots:
void loadIntoModel(std::shared_ptr data);
diff --git a/pglablib/catalog/PgAttribute.cpp b/pglablib/catalog/PgAttribute.cpp
index a43834e..5f0b4bc 100644
--- a/pglablib/catalog/PgAttribute.cpp
+++ b/pglablib/catalog/PgAttribute.cpp
@@ -3,18 +3,43 @@
#include "SqlFormattingUtils.h"
#include "PgClass.h"
#include "PgDatabaseCatalog.h"
+#include "PgSequenceContainer.h"
#include "PgTypeContainer.h"
#include "PgCollation.h"
#include "PgCollationContainer.h"
#include "Pgsql_oids.h"
+
+PgAttribute::Identity PgAttribute::getIdentity() const
+{
+ switch (identity)
+ {
+ case '\0': return Identity::None;
+ case 'a': return Identity::Always;
+ case 'd': return Identity::ByDefault;
+ }
+ assert(false); // we shouldn't get here
+ return {};
+}
+
+PgAttribute::Generated PgAttribute::getGenerated() const
+{
+ switch (generated)
+ {
+ case '\0': return Generated::None;
+ case 's': return Generated::Stored;
+ }
+ assert(false); // we shouldn't get here
+ return {};
+}
+
QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
{
// create: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// alter: column_name data_type [ COLLATE collation ] [ column_constraint [ ... ]
// constraints NULL/NOT NULL, DEFAULT, GENERATED other constraints will be ignored here a
QString type_str;
- if (isSerial()) {
+ if (isSerial() && identity == '\0') {
if (typid == Pgsql::int4_oid)
type_str = "SERIAL";
else if (typid == Pgsql::int8_oid)
@@ -35,16 +60,31 @@ QString PgAttribute::columnDefinition(const PgDatabaseCatalog &cat) const
if (notnull)
sql += " NOT NULL";
- if (hasdef && !isSerial())
- sql += " DEFAULT " % defaultValue;
-
- if (identity != '\0') {
+ auto identity = getIdentity();
+ auto generated = getGenerated();
+ if (identity != Identity::None) {
sql += " GENERATED ";
- if (identity == 'a') sql += "ALWAYS";
- else if (identity == 'd') sql += "BY DEFAULT";
+ if (identity == Identity::Always)
+ sql += "ALWAYS";
+ else if (identity == Identity::ByDefault)
+ sql += "BY DEFAULT";
sql += " AS IDENTITY";
+
+ auto sequence = cat.sequences()->getByObjectNsAndName(serschema, sername);
+ auto options_string = sequence->nonDefaultOptionsString();
+ if (!options_string.isEmpty())
+ sql += "(" % options_string % ")";
}
- // TODO sequence options might be missing
+ else if (generated != Generated::None)
+ {
+ if (generated == Generated::Stored)
+ sql += " GENERATED ALWAYS AS " % defaultValue % " STORED";
+ }
+ else
+ {
+ if (hasdef && !isSerial())
+ sql += " DEFAULT " % defaultValue;
+ }
return sql;
}
diff --git a/pglablib/catalog/PgAttribute.h b/pglablib/catalog/PgAttribute.h
index e7958ad..3898090 100644
--- a/pglablib/catalog/PgAttribute.h
+++ b/pglablib/catalog/PgAttribute.h
@@ -11,6 +11,16 @@ class PgDatabaseCatalog;
class PgAttribute {
public:
+ enum class Identity {
+ None,
+ Always,
+ ByDefault
+ };
+ enum class Generated {
+ None,
+ Stored
+ };
+
using Key = std::tuple;
Oid relid = InvalidOid;
@@ -23,12 +33,16 @@ 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
diff --git a/pglablib/catalog/PgAttributeContainer.cpp b/pglablib/catalog/PgAttributeContainer.cpp
index 5420a25..8ea91d7 100644
--- a/pglablib/catalog/PgAttributeContainer.cpp
+++ b/pglablib/catalog/PgAttributeContainer.cpp
@@ -16,7 +16,9 @@ SELECT attrelid, attname, atttypid, attstattarget,
cs.relname AS sername, ns.nspname AS serschema, d.description)__";
if (m_catalog.serverVersion() >= 100000)
q += ", attidentity";
- q += R"__(
+ if (m_catalog.serverVersion() >= 120000)
+ q += ", attgenerated";
+ q += R"__(
FROM pg_catalog.pg_attribute AS att
LEFT JOIN pg_attrdef AS def ON attrelid=adrelid AND attnum=adnum
LEFT JOIN (pg_depend JOIN pg_class cs ON classid='pg_class'::regclass AND objid=cs.oid AND cs.relkind='S') ON refobjid=att.attrelid AND refobjsubid=att.attnum
@@ -36,6 +38,8 @@ PgAttribute PgAttributeContainer::loadElem(const Pgsql::Row &row)
>> v.sername >> v.serschema >> v.description;
if (m_catalog.serverVersion() >= 100000)
col >> v.identity;
+ if (m_catalog.serverVersion() >= 120000)
+ col >> v.generated;
return v;
}
diff --git a/pglablib/catalog/PgContainer.h b/pglablib/catalog/PgContainer.h
index c572d87..85d940a 100644
--- a/pglablib/catalog/PgContainer.h
+++ b/pglablib/catalog/PgContainer.h
@@ -78,6 +78,36 @@ public:
return nullptr;
}
+ const T* getByObjectName(const QString &name) const
+ {
+ auto find_res = std::find_if(m_container.begin(), m_container.end(),
+ [&name](const T& item)
+ {
+ return item.objectName() == name;
+ });
+
+ if (find_res != m_container.end())
+ return &*find_res;
+
+ return nullptr;
+ }
+
+ const T* getByObjectNsAndName(const QString &ns, const QString &name) const
+ {
+ auto find_res = std::find_if(m_container.begin(), m_container.end(),
+ [&ns, &name](const T& item)
+ {
+ // check name first as it is less likely to pass
+ return item.objectName() == name
+ && item.nsName() == ns;
+ });
+
+ if (find_res != m_container.end())
+ return &*find_res;
+
+ return nullptr;
+ }
+
/// Retrieve element by index
///
/// This function throws when idx is out of range
diff --git a/pglablib/catalog/PgKeywordList.cpp b/pglablib/catalog/PgKeywordList.cpp
index 45965cd..e884222 100644
--- a/pglablib/catalog/PgKeywordList.cpp
+++ b/pglablib/catalog/PgKeywordList.cpp
@@ -5,119 +5,11 @@ namespace {
using KeywordHT = std::unordered_set;
- #define PG_KEYWORD(a,b,c) Keyword{a,c},
+ #define PG_KEYWORD(a,b,c,d) Keyword{a,c},
const KeywordHT _ScanKeywords = {
//const Keyword _ScanKeywords[] = {
- #include
- };
+ #include "kwlist.h"
- // The following keyword list are for plpgsql and are copied from the postgresql source.
- // src\pl\plpgsql\src\pl_scanner.c
-
- static const KeywordHT plpgsql_reserved_keywords = {
- PG_KEYWORD("all", K_ALL, RESERVED_KEYWORD)
- PG_KEYWORD("begin", K_BEGIN, RESERVED_KEYWORD)
- PG_KEYWORD("by", K_BY, RESERVED_KEYWORD)
- PG_KEYWORD("case", K_CASE, RESERVED_KEYWORD)
- PG_KEYWORD("declare", K_DECLARE, RESERVED_KEYWORD)
- PG_KEYWORD("else", K_ELSE, RESERVED_KEYWORD)
- PG_KEYWORD("end", K_END, RESERVED_KEYWORD)
- PG_KEYWORD("execute", K_EXECUTE, RESERVED_KEYWORD)
- PG_KEYWORD("for", K_FOR, RESERVED_KEYWORD)
- PG_KEYWORD("foreach", K_FOREACH, RESERVED_KEYWORD)
- PG_KEYWORD("from", K_FROM, RESERVED_KEYWORD)
- PG_KEYWORD("if", K_IF, RESERVED_KEYWORD)
- PG_KEYWORD("in", K_IN, RESERVED_KEYWORD)
- PG_KEYWORD("into", K_INTO, RESERVED_KEYWORD)
- PG_KEYWORD("loop", K_LOOP, RESERVED_KEYWORD)
- PG_KEYWORD("not", K_NOT, RESERVED_KEYWORD)
- PG_KEYWORD("null", K_NULL, RESERVED_KEYWORD)
- PG_KEYWORD("or", K_OR, RESERVED_KEYWORD)
- PG_KEYWORD("strict", K_STRICT, RESERVED_KEYWORD)
- PG_KEYWORD("then", K_THEN, RESERVED_KEYWORD)
- PG_KEYWORD("to", K_TO, RESERVED_KEYWORD)
- PG_KEYWORD("using", K_USING, RESERVED_KEYWORD)
- PG_KEYWORD("when", K_WHEN, RESERVED_KEYWORD)
- PG_KEYWORD("while", K_WHILE, RESERVED_KEYWORD)
- };
-
- static const KeywordHT plpgsql_unreserved_keywords = {
- PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD)
- PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
- PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
- PG_KEYWORD("assert", K_ASSERT, UNRESERVED_KEYWORD)
- PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
- PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD)
- PG_KEYWORD("collate", K_COLLATE, UNRESERVED_KEYWORD)
- PG_KEYWORD("column", K_COLUMN, UNRESERVED_KEYWORD)
- PG_KEYWORD("column_name", K_COLUMN_NAME, UNRESERVED_KEYWORD)
- PG_KEYWORD("constant", K_CONSTANT, UNRESERVED_KEYWORD)
- PG_KEYWORD("constraint", K_CONSTRAINT, UNRESERVED_KEYWORD)
- PG_KEYWORD("constraint_name", K_CONSTRAINT_NAME, UNRESERVED_KEYWORD)
- PG_KEYWORD("continue", K_CONTINUE, UNRESERVED_KEYWORD)
- PG_KEYWORD("current", K_CURRENT, UNRESERVED_KEYWORD)
- PG_KEYWORD("cursor", K_CURSOR, UNRESERVED_KEYWORD)
- PG_KEYWORD("datatype", K_DATATYPE, UNRESERVED_KEYWORD)
- PG_KEYWORD("debug", K_DEBUG, UNRESERVED_KEYWORD)
- PG_KEYWORD("default", K_DEFAULT, UNRESERVED_KEYWORD)
- PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
- PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD)
- PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
- PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD)
- PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD)
- PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
- PG_KEYWORD("error", K_ERROR, UNRESERVED_KEYWORD)
- PG_KEYWORD("exception", K_EXCEPTION, UNRESERVED_KEYWORD)
- PG_KEYWORD("exit", K_EXIT, UNRESERVED_KEYWORD)
- PG_KEYWORD("fetch", K_FETCH, UNRESERVED_KEYWORD)
- PG_KEYWORD("first", K_FIRST, UNRESERVED_KEYWORD)
- PG_KEYWORD("forward", K_FORWARD, UNRESERVED_KEYWORD)
- PG_KEYWORD("get", K_GET, UNRESERVED_KEYWORD)
- PG_KEYWORD("hint", K_HINT, UNRESERVED_KEYWORD)
- PG_KEYWORD("import", K_IMPORT, UNRESERVED_KEYWORD)
- PG_KEYWORD("info", K_INFO, UNRESERVED_KEYWORD)
- PG_KEYWORD("insert", K_INSERT, UNRESERVED_KEYWORD)
- PG_KEYWORD("is", K_IS, UNRESERVED_KEYWORD)
- PG_KEYWORD("last", K_LAST, UNRESERVED_KEYWORD)
- PG_KEYWORD("log", K_LOG, UNRESERVED_KEYWORD)
- PG_KEYWORD("message", K_MESSAGE, UNRESERVED_KEYWORD)
- PG_KEYWORD("message_text", K_MESSAGE_TEXT, UNRESERVED_KEYWORD)
- PG_KEYWORD("move", K_MOVE, UNRESERVED_KEYWORD)
- PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
- PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
- PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
- PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD)
- PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
- PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
- PG_KEYWORD("pg_context", K_PG_CONTEXT, UNRESERVED_KEYWORD)
- PG_KEYWORD("pg_datatype_name", K_PG_DATATYPE_NAME, UNRESERVED_KEYWORD)
- PG_KEYWORD("pg_exception_context", K_PG_EXCEPTION_CONTEXT, UNRESERVED_KEYWORD)
- PG_KEYWORD("pg_exception_detail", K_PG_EXCEPTION_DETAIL, UNRESERVED_KEYWORD)
- PG_KEYWORD("pg_exception_hint", K_PG_EXCEPTION_HINT, UNRESERVED_KEYWORD)
- PG_KEYWORD("print_strict_params", K_PRINT_STRICT_PARAMS, UNRESERVED_KEYWORD)
- PG_KEYWORD("prior", K_PRIOR, UNRESERVED_KEYWORD)
- PG_KEYWORD("query", K_QUERY, UNRESERVED_KEYWORD)
- PG_KEYWORD("raise", K_RAISE, UNRESERVED_KEYWORD)
- PG_KEYWORD("relative", K_RELATIVE, UNRESERVED_KEYWORD)
- PG_KEYWORD("result_oid", K_RESULT_OID, UNRESERVED_KEYWORD)
- PG_KEYWORD("return", K_RETURN, UNRESERVED_KEYWORD)
- PG_KEYWORD("returned_sqlstate", K_RETURNED_SQLSTATE, UNRESERVED_KEYWORD)
- PG_KEYWORD("reverse", K_REVERSE, UNRESERVED_KEYWORD)
- PG_KEYWORD("row_count", K_ROW_COUNT, UNRESERVED_KEYWORD)
- PG_KEYWORD("rowtype", K_ROWTYPE, UNRESERVED_KEYWORD)
- PG_KEYWORD("schema", K_SCHEMA, UNRESERVED_KEYWORD)
- PG_KEYWORD("schema_name", K_SCHEMA_NAME, UNRESERVED_KEYWORD)
- PG_KEYWORD("scroll", K_SCROLL, UNRESERVED_KEYWORD)
- PG_KEYWORD("slice", K_SLICE, UNRESERVED_KEYWORD)
- PG_KEYWORD("sqlstate", K_SQLSTATE, UNRESERVED_KEYWORD)
- PG_KEYWORD("stacked", K_STACKED, UNRESERVED_KEYWORD)
- PG_KEYWORD("table", K_TABLE, UNRESERVED_KEYWORD)
- PG_KEYWORD("table_name", K_TABLE_NAME, UNRESERVED_KEYWORD)
- PG_KEYWORD("type", K_TYPE, UNRESERVED_KEYWORD)
- PG_KEYWORD("use_column", K_USE_COLUMN, UNRESERVED_KEYWORD)
- PG_KEYWORD("use_variable", K_USE_VARIABLE, UNRESERVED_KEYWORD)
- PG_KEYWORD("variable_conflict", K_VARIABLE_CONFLICT, UNRESERVED_KEYWORD)
- PG_KEYWORD("warning", K_WARNING, UNRESERVED_KEYWORD)
};
}
@@ -131,96 +23,3 @@ const Keyword* getPgsqlKeyword(QString s)
return result;
}
-
-//t_SymbolSet g_Keywords = {
-// "a", "abort", "abs", "absent", "absolute", "access", "according", "action", "ada", "add",
-// "admin", "after", "aggregate", "all", "allocate", "also", "alter", "analyse", "analyze", "and",
-// "any", "are", "array", "array_agg", "array_max_cardinality", "as", "asc", "asensitive",
-// "assetion", "assignment", "asymmetric", "at", "atomic", "attribute", "attributes", "authorization", "avg",
-// "backward", "base64", "before", "begin", "begin_frame", "begin_partition", "bernoulli", "between", "bigint", "binary",
-// "bit", "bit_length", "blob", "blocked", "bom", "boolean", "both", "breadth", "buffer", "by",
-// "c", "cache", "call", "called", "cardinality", "cascade", "cascaded", "case", "cast",
-// "catalog", "catalog_name", "ceil", "ceiling", "chain", "char", "character", "characteristics",
-// "characters", "character_length", "character_set_catalog", "character_set_name", "character_set_schema",
-// "char_length", "check", "checkpoint", "class", "class_origin", "clob", "close", "cluster",
-// "coalesce", "cobol", "collate", "collation", "collation_catalog", "collation_name", "collation_schema",
-// "collect", "column", "columns", "column_name", "command_function", "command_function_code",
-// "comment", "comments", "commit", "committed", "concurrently", "condition", "condition_number",
-// "configuration", "conflict", "connect", "connection", "connection_name", "constraint", "constraints",
-// "constraint_catalog", "constraint_name", "constraint_schema", "constructor", "contains", "content",
-// "continue", "control", "conversion", "convert", "copy", "corr", "corresponding", "cost", "count",
-// "covar_pop", "covar_samp", "create", "cross", "csv", "cube", "cume_dist", "current", "current_catalog",
-// "current_date", "current_default_transform_group", "current_path", "current_role", "current_row",
-// "current_schema", "current_time", "current_timestamp", "current_transform_group_for_type",
-// "current_user", "cursor", "cursor_name", "cycle",
-// "data", "database", "datalink", "date", "datetime_interval_code", "datetime_interval_precision",
-// "day", "db", "deallocate", "dec", "decimal", "declare", "default", "defaults", "deferrable", "deferred",
-// "defined", "definer", "degree", "delete", "delimiter", "delimiters", "dense_rank", "depends", "depth",
-// "deref", "derived", "desc", "describe", "descriptor", "deterministic", "diagnostics", "dictionary",
-// "disable", "discard", "disconnect", "dispatch", "distinct", "dlnewcopy", "dlpreviouscopy", "dlurlcomplete",
-// "dlurlcompleteonly", "dlurlcompletewrite", "dlurlpatch", "dlurlpathonly", "dlurlpathwrite", "dlurlscheme",
-// "dlurlserver", "dlvalue", "do", "document", "domain", "double", "drop", "dynamic", "dynamic_function",
-// "dynamic_function_code",
-// "each", "element", "else", "empty", "enable", "encodign", "encrypted", "end", "end-exec", "end_frame",
-// "end_partition", "enforced", "enum", "equals", "escape", "event", "every", "except", "exception", "exclude",
-// "excluding", "exclusive", "exec", "execute", "exists", "exp", "explain", "expression", "extenstion",
-// "external", "extract", "false", "family", "fetch", "file", "filter", "final", "first", "first_value",
-// "flag", "float", "floor", "following", "for", "force", "foreign", "fortran", "forward", "found",
-// "frame_row", "free", "freeze", "from", "fs", "full", "function", "functions", "fusion",
-// "g", "general", "generated", "get", "global", "go" "goto", "grant", "granted", "greatest", "group",
-// "grouping", "groups", "handler", "having", "header", "hex", "hierarchy", "hold", "hour", "id", "identity",
-// "if", "ignore", "ilike", "immediate", "immediatly", "immutable", "implementation", "implicit", "import", "in",
-// "including", "increment", "indent", "index", "indexes", "indicator", "inherit", "inherits", "initially", "inline",
-// "inner", "inout", "input", "insensitive", "insert", "instance", "instantiable", "instead", "int", "integer",
-// "integrity", "intersect", "intersection", "interval", "into", "invoker", "is", "isnull", "isolation",
-// "join",
-// "k", "key", "key_member", "key_type",
-// "label", "lag", "language", "large", "last", "last_value", "lateral", "lead", "leading", "leakproof",
-// "least", "left", "length", "level", "library", "like", "like_regex", "limit", "link", "listen", "ln", "load", "local",
-// "localtime", "localtimestamp", "location", "locator", "lock", "locked", "logged", "lower",
-// "m", "map", "mapping", "match", "matched", "materialized", "max", "maxvalue", "max_cardinality", "member",
-// "merge", "message_length", "message_octet_length", "message_text", "method", "min", "minute", "minvalue",
-// "mod", "mode", "modifies", "module", "month", "more", "move", "multiset", "mumps",
-// "name", "namespace", "national", "natural", "nchar", "nclob", "nesting", "new", "next", "nfc", "nfd", "nfkc", "nkfd",
-// "nil", "no", "none", "normalize", "normalize", "not", "nothing", "notify", "notnull", "nowait", "nth_value", "ntile",
-// "null", "nullable", "nullif", "nulls", "number", "numeric",
-// "object", "occurrences_regex", "octets", "octet_length", "of", "off", "offset", "oids", "old", "on", "only", "open",
-// "operator", "option", "options", "or", "order", "ordering", "ordinality", "others", "out", "outer", "output", "over",
-// "overlaps", "overlay", "overriding", "owned", "owner",
-// "p", "pad", "parallel", "parameter", "parameter_mode", "parameter_name", "parameter_specific_catalog",
-// "parameter_specific_name", "parameter_specific_schema", "parser",
-// "partial", "partition", "pascal", "passing", "passthrough", "password", "path", "percent", "percentile_cont",
-// "percentile_disc", "percent_rank", "period", "permission", "placing", "plans", "pli", "policy", "portion",
-// "position", "position_regex", "power", "precedes", "preceding", "precision", "prepare", "prepared", "preserve",
-// "primary", "prior", "privileges", "procedural", "procedure", "program", "public",
-// "quote", "range", "rank", "read", "reads", "real", "reassign", "recheck", "recovery", "recursive", "ref",
-// "references", "referencing", "refresh", "regr_avgx", "regr_avgy", "regr_count", "regr_intercept", "regr_r2",
-// "regr_slope", "regr_sxx", "regr_sxy", "regr_syy", "reindex", "relative", "release", "rename", "repeatable",
-// "replace", "replica", "requiring", "reset", "respect", "restart", "restore", "restrict", "result", "return",
-// "returned_cardinality", "returned_length", "returned_octet_length", "returned_sqlstate", "returning", "returns",
-// "revoke", "right", "role", "rollback", "rollup", "routine", "routine_catalog", "routine_name", "routine_schema",
-// "row", "rows", "row_count", "row_number", "rule",
-// "savepoint", "scale", "schema", "schema_name", "scope", "scope_catalog", "scope_name", "scope_schema", "scroll",
-// "search", "second", "section", "security", "select", "selective", "self", "sensitive", "sequence", "sequences",
-// "serializable", "server", "server_name", "session", "session_user", "set", "setof", "sets", "share", "show",
-// "similar", "simple", "size", "skip", "smallint", "snapshot", "some", "source", "space", "specific", "specifictype",
-// "specific_name", "sql", "sqlcode", "sqlerror", "sqlexception", "sqlstate", "sqlwarning", "sqrt", "stable",
-// "standalone", "start", "state", "statement", "static", "statistics", "stddev_pop", "stddev_samp", "stdin", "stdout",
-// "storage", "strict", "strip", "structure", "style", "subclass_origin", "submultiset", "substring", "substring_regex",
-// "succeeds", "sum", "symmetric", "sysid", "system", "system_time", "system_user",
-// "t", "table", "tables", "tablesample", "tablespace", "table_name", "temp", "template", "temporary", "text", "then",
-// "ties", "time", "timestamp", "timezone_hour", "timezone_minute", "to", "token", "top_level_count", "trailing",
-// "transaction", "transaction_committed", "transaction_rolled_back", "transaction_active", "transform", "transforms",
-// "translate", "translate_regex", "translation", "treat", "trigger", "trigger_catalog", "trigger_name", "trigger_schema",
-// "trim", "trim_array", "true", "truncate", "trusted", "type", "types", "uescape", "unbounded", "uncommitted", "under",
-// "unencrypted", "union", "unique", "unknown", "unlink", "unlisten", "unlogged", "unnamed", "unnest", "until", "untyped",
-// "update", "upper", "uri", "usage", "user", "user_defined_type_catalog", "user_defined_type_code",
-// "user_defined_type_name", "user_defined_type_schema", "using",
-// "vacuum", "valid", "validate", "validator", "value", "values", "value_of", "varbinary", "varchar", "variadic",
-// "varying", "var_pop", "var_samp", "verbose", "version", "versioning", "view", "views", "volatile",
-// "when", "whenever", "where", "whitespace", "width_bucket", "window", "with", "within", "without", "work", "wrapper",
-// "write", "xml", "xmlagg", "xmlattributes", "xmlbinary", "xmlcast", "xmlcomment", "xmlconcat", "xmldeclaration",
-// "xmldocument", "xmlelement", "xmlexists", "xmlforest", "xmliterate", "xmlnamespaces", "xmlparse", "xmlpi",
-// "xmlquery", "xmlroot", "xmlschema", "xmlserialize", "xmltable", "xmltext", "xmlvalidate", "year", "yes", "zone"
-//};
-
diff --git a/pglablib/catalog/PgSequence.cpp b/pglablib/catalog/PgSequence.cpp
index 2355b16..478f162 100644
--- a/pglablib/catalog/PgSequence.cpp
+++ b/pglablib/catalog/PgSequence.cpp
@@ -19,5 +19,60 @@ QString PgSequence::createSql() const
// [ OWNED BY { table_name.column_name | NONE } ]
sql += ";";
- return sql;
+ return sql;
+}
+
+int64_t PgSequence::defaultMin() const
+{
+ if (direction() == Ascending)
+ return 1;
+
+ if (typid == Pgsql::int8_oid)
+ return std::numeric_limits::min();
+ return std::numeric_limits::min();
+}
+
+int64_t PgSequence::defaultMax() const
+{
+ if (direction() == Descending)
+ return -1;
+
+ if (typid == Pgsql::int8_oid)
+ return std::numeric_limits::max();
+ return std::numeric_limits::max();
+}
+
+int64_t PgSequence::defaultStart() const
+{
+ if (direction() == Descending)
+ return max;
+ return min;
+}
+
+int64_t PgSequence::defaultIncrement() const
+{
+ // Direction should not be considered here
+ // a negative increment is what makes it a descending sequence
+ return 1;
+}
+
+int64_t PgSequence::defaultCache() const
+{
+ return 1;
+}
+
+QString PgSequence::nonDefaultOptionsString() const
+{
+ QString options;
+ if (increment != defaultIncrement())
+ options += QString("INCREMENT %1 ").arg(increment);
+ if (min != defaultMin())
+ options += QString("MINVALUE %1 ").arg(min);
+ if (max != defaultMax())
+ options += QString("MAXVALUE %1 ").arg(max);
+ if (start != defaultStart())
+ options += QString("START %1 ").arg(start);
+ if (cache != defaultCache())
+ options += QString("CACHE %1 ").arg(cache);
+ return options.trimmed();
}
diff --git a/pglablib/catalog/PgSequence.h b/pglablib/catalog/PgSequence.h
index 92f4a78..1d1f155 100644
--- a/pglablib/catalog/PgSequence.h
+++ b/pglablib/catalog/PgSequence.h
@@ -5,23 +5,44 @@
class PgSequence: public PgClass {
public:
+ enum Direction {
+ Ascending,
+ Descending
+ };
+
+ // values that define the trigger
+ // in recent versions from the catalog
Oid typid = InvalidOid;
- int64_t last = 0;
int64_t start = 0;
int64_t increment = 0;
int64_t max = 0;
int64_t min = 0;
int64_t cache = 0;
- int64_t log = 0;
bool cycled = false;
- bool called = false;
bool priv_usage = false;
bool priv_select = false;
bool priv_update = false;
+ // Sequence state data, retrieved by SELECTing the sequence
+ int64_t last = 0;
+ int64_t log = 0; // shows how many fetches are left before new wal log
+ bool called = false;
+
using PgClass::PgClass;
QString createSql() const;
+
+ Direction direction() const
+ {
+ return increment < 0 ? Descending : Ascending;
+ }
+ int64_t defaultMin() const;
+ int64_t defaultMax() const;
+ int64_t defaultStart() const;
+ int64_t defaultIncrement() const;
+ int64_t defaultCache() const;
+ // other option defaults are 1
+ QString nonDefaultOptionsString() const;
};
#endif // PGSEQUENCE_H
diff --git a/pglablib/catalog/kwlist.h b/pglablib/catalog/kwlist.h
new file mode 100644
index 0000000..f836acf
--- /dev/null
+++ b/pglablib/catalog/kwlist.h
@@ -0,0 +1,484 @@
+/*-------------------------------------------------------------------------
+ *
+ * kwlist.h
+ *
+ * The keyword lists are kept in their own source files for use by
+ * automatic tools. The exact representation of a keyword is determined
+ * by the PG_KEYWORD macro, which is not defined in this file; it can
+ * be defined by the caller for special purposes.
+ *
+ * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/include/parser/kwlist.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+/* there is deliberately not an #ifndef KWLIST_H here */
+
+/*
+ * List of keyword (name, token-value, category, bare-label-status) entries.
+ *
+ * Note: gen_keywordlist.pl requires the entries to appear in ASCII order.
+ */
+
+/* name, value, category, is-bare-label */
+PG_KEYWORD("abort", ABORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("absolute", ABSOLUTE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("access", ACCESS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("action", ACTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("add", ADD_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("admin", ADMIN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("after", AFTER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("aggregate", AGGREGATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("all", ALL, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("also", ALSO, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("alter", ALTER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("always", ALWAYS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("analyse", ANALYSE, RESERVED_KEYWORD, BARE_LABEL) /* British spelling */
+PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("and", AND, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("any", ANY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("as", AS, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("asc", ASC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("asensitive", ASENSITIVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("at", AT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("atomic", ATOMIC, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("attach", ATTACH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("before", BEFORE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("begin", BEGIN_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("between", BETWEEN, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("bigint", BIGINT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("binary", BINARY, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("bit", BIT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("boolean", BOOLEAN_P, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("both", BOTH, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("breadth", BREADTH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("by", BY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cache", CACHE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("call", CALL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("called", CALLED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cascade", CASCADE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cascaded", CASCADED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("case", CASE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cast", CAST, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("catalog", CATALOG_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("chain", CHAIN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("char", CHAR_P, COL_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("character", CHARACTER, COL_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("characteristics", CHARACTERISTICS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("check", CHECK, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("checkpoint", CHECKPOINT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("class", CLASS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("close", CLOSE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cluster", CLUSTER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("coalesce", COALESCE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("collate", COLLATE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("collation", COLLATION, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("column", COLUMN, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("columns", COLUMNS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("comment", COMMENT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("comments", COMMENTS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("commit", COMMIT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("committed", COMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("compression", COMPRESSION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("concurrently", CONCURRENTLY, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("configuration", CONFIGURATION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("conflict", CONFLICT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("connection", CONNECTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("constraint", CONSTRAINT, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("constraints", CONSTRAINTS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("content", CONTENT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("continue", CONTINUE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("conversion", CONVERSION_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("copy", COPY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cost", COST, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("create", CREATE, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("cross", CROSS, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("csv", CSV, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cube", CUBE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current", CURRENT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_catalog", CURRENT_CATALOG, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_date", CURRENT_DATE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_role", CURRENT_ROLE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_schema", CURRENT_SCHEMA, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_time", CURRENT_TIME, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_timestamp", CURRENT_TIMESTAMP, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("current_user", CURRENT_USER, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cursor", CURSOR, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("cycle", CYCLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("data", DATA_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("database", DATABASE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("day", DAY_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("deallocate", DEALLOCATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("dec", DEC, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("decimal", DECIMAL_P, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("declare", DECLARE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("default", DEFAULT, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("defaults", DEFAULTS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("deferrable", DEFERRABLE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("deferred", DEFERRED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("definer", DEFINER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("delete", DELETE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("depends", DEPENDS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("depth", DEPTH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("desc", DESC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("distinct", DISTINCT, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("do", DO, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("document", DOCUMENT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("else", ELSE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("encrypted", ENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("end", END_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("enum", ENUM_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("escape", ESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("event", EVENT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("except", EXCEPT, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("exclude", EXCLUDE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("excluding", EXCLUDING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("exclusive", EXCLUSIVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("execute", EXECUTE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("exists", EXISTS, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("explain", EXPLAIN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("expression", EXPRESSION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("extension", EXTENSION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("external", EXTERNAL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("extract", EXTRACT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("false", FALSE_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("family", FAMILY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("fetch", FETCH, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("filter", FILTER, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("finalize", FINALIZE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("first", FIRST_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("float", FLOAT_P, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("following", FOLLOWING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("for", FOR, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("force", FORCE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("foreign", FOREIGN, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("forward", FORWARD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("freeze", FREEZE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("from", FROM, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("full", FULL, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("function", FUNCTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("functions", FUNCTIONS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("generated", GENERATED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("global", GLOBAL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("grant", GRANT, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("granted", GRANTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("greatest", GREATEST, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("group", GROUP_P, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("grouping", GROUPING, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("groups", GROUPS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("handler", HANDLER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("having", HAVING, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("header", HEADER_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("hold", HOLD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("hour", HOUR_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("identity", IDENTITY_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("if", IF_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("immediate", IMMEDIATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("immutable", IMMUTABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("implicit", IMPLICIT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("import", IMPORT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("in", IN_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("include", INCLUDE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("inherit", INHERIT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("inherits", INHERITS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("initially", INITIALLY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("inline", INLINE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("inner", INNER_P, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("inout", INOUT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("input", INPUT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("insensitive", INSENSITIVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("insert", INSERT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("instead", INSTEAD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("int", INT_P, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("integer", INTEGER, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("intersect", INTERSECT, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("interval", INTERVAL, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("into", INTO, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("invoker", INVOKER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("is", IS, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("isnull", ISNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("isolation", ISOLATION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("join", JOIN, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("key", KEY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("label", LABEL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("language", LANGUAGE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("large", LARGE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("last", LAST_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("lateral", LATERAL_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("listen", LISTEN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("load", LOAD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("local", LOCAL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("localtime", LOCALTIME, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("localtimestamp", LOCALTIMESTAMP, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("location", LOCATION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("lock", LOCK_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("month", MONTH_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("move", MOVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("name", NAME_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("names", NAMES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("national", NATIONAL, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("natural", NATURAL, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nchar", NCHAR, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("new", NEW, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("next", NEXT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nfc", NFC, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nfd", NFD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nfkc", NFKC, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nfkd", NFKD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("no", NO, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("none", NONE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("normalize", NORMALIZE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("normalized", NORMALIZED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("not", NOT, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nothing", NOTHING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("notify", NOTIFY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("notnull", NOTNULL, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("nowait", NOWAIT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("null", NULL_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nullif", NULLIF, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("nulls", NULLS_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("numeric", NUMERIC, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("object", OBJECT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("of", OF, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("off", OFF, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("offset", OFFSET, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("oids", OIDS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("old", OLD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("on", ON, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("only", ONLY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("operator", OPERATOR, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("option", OPTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("options", OPTIONS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("or", OR, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("order", ORDER, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("ordinality", ORDINALITY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("others", OTHERS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("out", OUT_P, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("outer", OUTER_P, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("over", OVER, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("overlaps", OVERLAPS, TYPE_FUNC_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("overlay", OVERLAY, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("overriding", OVERRIDING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("plans", PLANS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("policy", POLICY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("position", POSITION, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("preceding", PRECEDING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("precision", PRECISION, COL_NAME_KEYWORD, AS_LABEL)
+PG_KEYWORD("prepare", PREPARE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("prepared", PREPARED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("preserve", PRESERVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("primary", PRIMARY, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("prior", PRIOR, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("privileges", PRIVILEGES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("procedural", PROCEDURAL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("procedure", PROCEDURE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("procedures", PROCEDURES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("program", PROGRAM, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("publication", PUBLICATION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("quote", QUOTE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("range", RANGE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("read", READ, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("real", REAL, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("reassign", REASSIGN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("recheck", RECHECK, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("recursive", RECURSIVE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("ref", REF, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("references", REFERENCES, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("referencing", REFERENCING, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("refresh", REFRESH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("reindex", REINDEX, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("relative", RELATIVE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("release", RELEASE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("rename", RENAME, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("repeatable", REPEATABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("replace", REPLACE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("replica", REPLICA, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("reset", RESET, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("restart", RESTART, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("restrict", RESTRICT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("return", RETURN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("returning", RETURNING, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("returns", RETURNS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("revoke", REVOKE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("right", RIGHT, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("role", ROLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("rollback", ROLLBACK, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("rollup", ROLLUP, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("routine", ROUTINE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("routines", ROUTINES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("row", ROW, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("schemas", SCHEMAS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("search", SEARCH, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("second", SECOND_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("security", SECURITY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("select", SELECT, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("sequence", SEQUENCE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("sequences", SEQUENCES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("serializable", SERIALIZABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("server", SERVER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("session", SESSION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("session_user", SESSION_USER, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("set", SET, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("setof", SETOF, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("sets", SETS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("share", SHARE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("show", SHOW, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("similar", SIMILAR, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("simple", SIMPLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("skip", SKIP, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("smallint", SMALLINT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("snapshot", SNAPSHOT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("some", SOME, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("sql", SQL_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("stable", STABLE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("standalone", STANDALONE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("start", START, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("statement", STATEMENT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("statistics", STATISTICS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("stdin", STDIN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("stored", STORED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("subscription", SUBSCRIPTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("support", SUPPORT, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("system", SYSTEM_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("table", TABLE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("tables", TABLES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("tablesample", TABLESAMPLE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("tablespace", TABLESPACE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("temp", TEMP, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("template", TEMPLATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("temporary", TEMPORARY, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("then", THEN, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("ties", TIES, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("time", TIME, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("to", TO, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("trailing", TRAILING, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("transaction", TRANSACTION, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("transform", TRANSFORM, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("treat", TREAT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("trigger", TRIGGER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("trim", TRIM, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("true", TRUE_P, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("truncate", TRUNCATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("trusted", TRUSTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("type", TYPE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("types", TYPES_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("uescape", UESCAPE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("unbounded", UNBOUNDED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("uncommitted", UNCOMMITTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("unencrypted", UNENCRYPTED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("union", UNION, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("unique", UNIQUE, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("unknown", UNKNOWN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("unlisten", UNLISTEN, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("unlogged", UNLOGGED, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("until", UNTIL, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("update", UPDATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("user", USER, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("using", USING, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("vacuum", VACUUM, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("valid", VALID, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("validate", VALIDATE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("validator", VALIDATOR, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("value", VALUE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("values", VALUES, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("varchar", VARCHAR, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("variadic", VARIADIC, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("varying", VARYING, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("version", VERSION_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("view", VIEW, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("views", VIEWS, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("volatile", VOLATILE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("when", WHEN, RESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("where", WHERE, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("whitespace", WHITESPACE_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("window", WINDOW, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("with", WITH, RESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("within", WITHIN, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("without", WITHOUT, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("work", WORK, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("wrapper", WRAPPER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("write", WRITE, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xml", XML_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlattributes", XMLATTRIBUTES, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlconcat", XMLCONCAT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlelement", XMLELEMENT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlexists", XMLEXISTS, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlforest", XMLFOREST, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlnamespaces", XMLNAMESPACES, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlparse", XMLPARSE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlpi", XMLPI, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlroot", XMLROOT, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmlserialize", XMLSERIALIZE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("xmltable", XMLTABLE, COL_NAME_KEYWORD, BARE_LABEL)
+PG_KEYWORD("year", YEAR_P, UNRESERVED_KEYWORD, AS_LABEL)
+PG_KEYWORD("yes", YES_P, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("zone", ZONE, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/releasenotes/notes/generated-support-1528dad543a7525f.yaml b/releasenotes/notes/generated-support-1528dad543a7525f.yaml
new file mode 100644
index 0000000..2bf6c15
--- /dev/null
+++ b/releasenotes/notes/generated-support-1528dad543a7525f.yaml
@@ -0,0 +1,15 @@
+---
+features:
+ - |
+ Full support for identity and generated columns. This includes recognizing
+ the new keywords, displaying column defaults as generated, showing the
+ correct SQL to recreate the objects.
+ The CRUD blocks editing of generated always columns.
+issues:
+ - |
+ From a CRUD tab it is currently not possible to override an ALWAYS AS IDENTITY
+ column with OVERRRIDING SYSTEM VALUE.
+ - |
+ When all columns are GENERATED ALWAYS you cannot add a new row from a CRUD tab
+ thought technically it is possible to INSERT a row by using DEFAULT.
+
\ No newline at end of file
From c942d0a4467630dc5488a5238b04d02ec56cffc0 Mon Sep 17 00:00:00 2001
From: eelke
Date: Tue, 6 Sep 2022 13:19:09 +0200
Subject: [PATCH 27/64] More documentation
---
docs/assets/custom.css | 4 ++
docs/conf.py | 6 ++-
docs/databasewindow/index.rst | 78 +++++++++++++++++++++++++++++++++++
docs/index.rst | 4 +-
docs/preface.rst | 25 +++++++++++
5 files changed, 115 insertions(+), 2 deletions(-)
create mode 100644 docs/assets/custom.css
create mode 100644 docs/databasewindow/index.rst
create mode 100644 docs/preface.rst
diff --git a/docs/assets/custom.css b/docs/assets/custom.css
new file mode 100644
index 0000000..623e050
--- /dev/null
+++ b/docs/assets/custom.css
@@ -0,0 +1,4 @@
+.menuselection {
+ font-weight: 100
+
+}
\ No newline at end of file
diff --git a/docs/conf.py b/docs/conf.py
index ae3d2cb..9356f1f 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -53,4 +53,8 @@ html_theme = 'furo'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = []
+html_static_path = ['assets']
+
+html_css_files = [
+ 'custom.css'
+]
\ No newline at end of file
diff --git a/docs/databasewindow/index.rst b/docs/databasewindow/index.rst
new file mode 100644
index 0000000..cc98557
--- /dev/null
+++ b/docs/databasewindow/index.rst
@@ -0,0 +1,78 @@
+###############
+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
+======
+
+
+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.
diff --git a/docs/index.rst b/docs/index.rst
index cf75cbd..aa39253 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -4,15 +4,17 @@
contain the root `toctree` directive.
pgLab User Manual
-====================================================
+=================
.. toctree::
:maxdepth: 3
:caption: Contents:
+ preface
releasenotes
installation
connectionmanager/index
+ databasewindow/index
internals
Indices and tables
diff --git a/docs/preface.rst b/docs/preface.rst
new file mode 100644
index 0000000..8bbca97
--- /dev/null
+++ b/docs/preface.rst
@@ -0,0 +1,25 @@
+#######
+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:
+* seperation of different databases into seperate 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.
From 92f924f7380716b211adf880b8cd10055bc5559f Mon Sep 17 00:00:00 2001
From: eelke
Date: Thu, 8 Sep 2022 09:42:51 +0200
Subject: [PATCH 28/64] documentation
---
docs/databasewindow/index.rst | 22 ++++++++++++++++++++++
docs/installation.rst | 26 +++++++++++++++++++++++++-
docs/internals.rst | 1 +
docs/preface.rst | 3 ++-
4 files changed, 50 insertions(+), 2 deletions(-)
diff --git a/docs/databasewindow/index.rst b/docs/databasewindow/index.rst
index cc98557..b2fb251 100644
--- a/docs/databasewindow/index.rst
+++ b/docs/databasewindow/index.rst
@@ -60,6 +60,7 @@ 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.
@@ -76,3 +77,24 @@ 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
diff --git a/docs/installation.rst b/docs/installation.rst
index a7d69ac..8d52bd0 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -11,8 +11,32 @@ Windows
Downloads can be found `here
`_.
+
+Installation
+============
+
Unpack the contents of the 7zip archive to a folder of your choosing for
-instance `C:\\Program files\\pgLab`. You can run the pgLab.exe from there. If
+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 `_.
+
+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.
diff --git a/docs/internals.rst b/docs/internals.rst
index 697234b..6c7e9ce 100644
--- a/docs/internals.rst
+++ b/docs/internals.rst
@@ -9,3 +9,4 @@ User data
All connection configuration information is stored in
`%AppData%\\pglab\\pglab\\pglabuser.db` this is an sqlite database.
+
diff --git a/docs/preface.rst b/docs/preface.rst
index 8bbca97..006c696 100644
--- a/docs/preface.rst
+++ b/docs/preface.rst
@@ -13,7 +13,8 @@ you can ofcourse execute DDL to create anything you like.
but it has currently no priority for me.)
The strong point of pgLab are:
-* seperation of different databases into seperate windows.
+
+* 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.
From 0911d2471e05c8f1904472255de2a4d062821c42 Mon Sep 17 00:00:00 2001
From: eelke
Date: Thu, 8 Sep 2022 09:49:17 +0200
Subject: [PATCH 29/64] Document fix from 9277731c
---
releasenotes/notes/generated-support-1528dad543a7525f.yaml | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/releasenotes/notes/generated-support-1528dad543a7525f.yaml b/releasenotes/notes/generated-support-1528dad543a7525f.yaml
index 2bf6c15..6387e72 100644
--- a/releasenotes/notes/generated-support-1528dad543a7525f.yaml
+++ b/releasenotes/notes/generated-support-1528dad543a7525f.yaml
@@ -12,4 +12,7 @@ issues:
- |
When all columns are GENERATED ALWAYS you cannot add a new row from a CRUD tab
thought technically it is possible to INSERT a row by using DEFAULT.
-
\ No newline at end of file
+fixes:
+ - |
+ Corrected the numbering column it should start with 1. This caused the delete
+ message from crud to be one of in it's lising of rows about to be deleted.
From 7c88716ab7567ada1c82595d886ca3360a70b4a2 Mon Sep 17 00:00:00 2001
From: eelke
Date: Tue, 20 Sep 2022 19:15:28 +0200
Subject: [PATCH 30/64] Fix links
---
pglab/About.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pglab/About.cpp b/pglab/About.cpp
index 75c1b84..fd076f2 100644
--- a/pglab/About.cpp
+++ b/pglab/About.cpp
@@ -8,7 +8,7 @@
void ShowAboutDialog(QWidget *parent)
{
QString pgLabVersionString = "0.2";
- QString releaseNotesUrl = "file:///" + QCoreApplication::applicationDirPath() + "\\releasenotes.html";
+ QString releaseNotesUrl = "https://eelke.gitlab.io/pgLab/releasenotes.html";
QString year = QString::fromLatin1(__DATE__, 11).right(4);
QMessageBox::about(parent, "pgLab", QMessageBox::tr(
"Version %3
"
@@ -26,5 +26,5 @@ void ShowAboutDialog(QWidget *parent)
void OpenManual()
{
- QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/#pglab-user-manual"));
+ QDesktopServices::openUrl(QString("https://eelke.gitlab.io/pgLab/"));
}
From e68f17432c7bdacf732bc7560e9f05c64f8ebfdd Mon Sep 17 00:00:00 2001
From: eelke
Date: Tue, 20 Sep 2022 19:23:25 +0200
Subject: [PATCH 31/64] the job for gitlab pages must be called pages
---
.gitlab-ci.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 8562ad8..c9f8a89 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,7 +25,7 @@ test-docs:
only:
- branches
-deploy:
+pages:
stage: deploy
extends:
- .install_sphinx
From f253a6c9ff501ae7f04a3cb016b1bb714f88e433 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 2 Oct 2022 12:11:43 +0200
Subject: [PATCH 32/64] Move version string into Config header.
Makes it easier to update the version number.
---
pglab/About.cpp | 3 ++-
pglab/Config.h | 4 ++++
pglab/pglab.pro | 1 +
3 files changed, 7 insertions(+), 1 deletion(-)
create mode 100644 pglab/Config.h
diff --git a/pglab/About.cpp b/pglab/About.cpp
index fd076f2..ce5e8ad 100644
--- a/pglab/About.cpp
+++ b/pglab/About.cpp
@@ -1,4 +1,5 @@
#include "About.h"
+#include "Config.h"
#include
#include
#include
@@ -7,7 +8,7 @@
void ShowAboutDialog(QWidget *parent)
{
- QString pgLabVersionString = "0.2";
+ 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(
diff --git a/pglab/Config.h b/pglab/Config.h
new file mode 100644
index 0000000..c0accd3
--- /dev/null
+++ b/pglab/Config.h
@@ -0,0 +1,4 @@
+#pragma once
+
+#define PGLAB_VERSION_STRING "0.3"
+
diff --git a/pglab/pglab.pro b/pglab/pglab.pro
index 3bc5b30..2c24c63 100644
--- a/pglab/pglab.pro
+++ b/pglab/pglab.pro
@@ -93,6 +93,7 @@ SOURCES += main.cpp\
HEADERS += \
About.h \
+ Config.h \
catalog/delegates/IconColumnDelegate.h \
catalog/models/BaseTableModel.h \
catalog/models/ColumnTableModel.h \
From f3f1d47f7d7f18bb0cb352c89a90de2b1574d33b Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 2 Oct 2022 12:20:36 +0200
Subject: [PATCH 33/64] connection details in window title
---
pglab/DatabaseWindow.cpp | 2 +-
.../connection-details-in-window-title-3628d3263f7beb46.yaml | 4 ++++
2 files changed, 5 insertions(+), 1 deletion(-)
create mode 100644 releasenotes/notes/connection-details-in-window-title-3628d3263f7beb46.yaml
diff --git a/pglab/DatabaseWindow.cpp b/pglab/DatabaseWindow.cpp
index c003c86..e1c1a3c 100644
--- a/pglab/DatabaseWindow.cpp
+++ b/pglab/DatabaseWindow.cpp
@@ -105,7 +105,7 @@ void DatabaseWindow::setConfig(const ConnectionConfig &config)
try
{
QString title = "pglab - ";
- title += m_config.name();
+ title += m_config.makeLongDescription();
setWindowTitle(title);
auto cfg = m_config;
diff --git a/releasenotes/notes/connection-details-in-window-title-3628d3263f7beb46.yaml b/releasenotes/notes/connection-details-in-window-title-3628d3263f7beb46.yaml
new file mode 100644
index 0000000..46b7305
--- /dev/null
+++ b/releasenotes/notes/connection-details-in-window-title-3628d3263f7beb46.yaml
@@ -0,0 +1,4 @@
+---
+other:
+ - |
+ The database connection window now contains connection details in the title.
From 0cd019db92a957fa937b7cc385b04dba18ba52eb Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 7 Jan 2023 07:41:58 +0100
Subject: [PATCH 34/64] Fix for $-quoted strings
Note this does not solve all issues because we are tokenizing contents of strings of which we do not know they contains SQL
when the string is actually not SQL and contains $ the tokenizer gets confused.
---
core/SqlLexer.cpp | 7 +++++++
tests/pglabtests/tst_SqlLexer.cpp | 30 ++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/core/SqlLexer.cpp b/core/SqlLexer.cpp
index fb40150..55cf892 100644
--- a/core/SqlLexer.cpp
+++ b/core/SqlLexer.cpp
@@ -255,6 +255,13 @@ bool SqlLexer::parseDoubleQuotedIdentifier(int startpos, int &length, BasicToken
bool SqlLexer::parseDollarQuote(int startpos, int &length, BasicTokenType &tokentype, QString &out)
{
QChar c = nextChar();
+ if (c == '$') {
+ tokentype = BasicTokenType::DollarQuote;
+ length = m_pos - startpos;
+ out = m_block.mid(startpos, length);
+ return true;
+ }
+
if (c.isDigit()) {
for (;;) {
c = peekChar();
diff --git a/tests/pglabtests/tst_SqlLexer.cpp b/tests/pglabtests/tst_SqlLexer.cpp
index bcdb03e..0be623d 100644
--- a/tests/pglabtests/tst_SqlLexer.cpp
+++ b/tests/pglabtests/tst_SqlLexer.cpp
@@ -19,6 +19,36 @@ TEST(SqlLexer, emptyInput)
ASSERT_THAT(tokentype, Eq(BasicTokenType::End));
}
+TEST(SqlLexer, emptyDollarQuote)
+{
+ QString input = "$$";
+ SqlLexer lexer(input, LexerState::Null);
+
+ int startpos = -1, length = -1;
+ BasicTokenType tokentype = BasicTokenType::None;
+ QString out;
+ lexer.nextBasicToken(startpos, length, tokentype, out);
+
+ ASSERT_THAT(startpos, Eq(0));
+ ASSERT_THAT("$$", Eq(out));
+ ASSERT_THAT(tokentype, Eq(BasicTokenType::DollarQuote));
+}
+
+TEST(SqlLexer, filledDollarQuote)
+{
+ QString input = "$body$";
+ SqlLexer lexer(input, LexerState::Null);
+
+ int startpos = -1, length = -1;
+ BasicTokenType tokentype = BasicTokenType::None;
+ QString out;
+ lexer.nextBasicToken(startpos, length, tokentype, out);
+
+ ASSERT_THAT(startpos, Eq(0));
+ ASSERT_THAT("$body$", Eq(out));
+ ASSERT_THAT(tokentype, Eq(BasicTokenType::DollarQuote));
+}
+
TEST(SqlLexer, lexer)
{
QString input = " SELECT ";
From 33319e3461e8c694c29254c994eadcba0ef84c60 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 7 Jan 2023 07:44:33 +0100
Subject: [PATCH 35/64] Fix reading from catalog so that information about
declarative partitioning is read correctly
(View and SQL generation still need fixes)
---
pglab/catalog/models/TablesTableModel.cpp | 17 +++++++++++++--
pglablib/catalog/PgClass.cpp | 26 ++++++++++++++++-------
pglablib/catalog/PgClass.h | 5 ++++-
pglablib/catalog/PgClassContainer.cpp | 7 +++++-
4 files changed, 43 insertions(+), 12 deletions(-)
diff --git a/pglab/catalog/models/TablesTableModel.cpp b/pglab/catalog/models/TablesTableModel.cpp
index 4a07b9f..fd285ed 100644
--- a/pglab/catalog/models/TablesTableModel.cpp
+++ b/pglab/catalog/models/TablesTableModel.cpp
@@ -33,6 +33,20 @@ void TablesTableModel::setCatalog(std::shared_ptr 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();
@@ -48,8 +62,7 @@ void TablesTableModel::refresh()
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) {
+ if (TableLike(e.kind)) {
switch (m_namespaceFilter) {
case NamespaceFilter::User:
add = !e.ns().isSystemCatalog();
diff --git a/pglablib/catalog/PgClass.cpp b/pglablib/catalog/PgClass.cpp
index 5dec586..90b1b71 100644
--- a/pglablib/catalog/PgClass.cpp
+++ b/pglablib/catalog/PgClass.cpp
@@ -57,6 +57,14 @@ void operator<<(RelKind &s, const Pgsql::Value &v)
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");
}
}
@@ -81,14 +89,16 @@ QString PgClass::typeName() const
{
switch (kind)
{
- case RelKind::Table: return "TABLE";
- case RelKind::Index: return "INDEX";
- case RelKind::Sequence: return "SEQUENCE";
- case RelKind::View: return "VIEW";
- case RelKind::MaterializedView: return "MATERIALIZED VIEW";
- case RelKind::Composite: return "COMPOSITE";
- case RelKind::Toast: return "TOAST";
- case RelKind::ForeignTable: return "FOREIGN TABLE";
+ case RelKind::Table: return "TABLE";
+ case RelKind::Index: return "INDEX";
+ case RelKind::Sequence: return "SEQUENCE";
+ case RelKind::View: return "VIEW";
+ case RelKind::MaterializedView: return "MATERIALIZED VIEW";
+ case RelKind::Composite: return "COMPOSITE";
+ case RelKind::Toast: return "TOAST";
+ case RelKind::ForeignTable: return "FOREIGN TABLE";
+ case RelKind::PartitionedTable: return "PARTITIONED TABLE";
+ case RelKind::PartitionedIndex: return "PARTITIONED INDEX";
}
throw std::runtime_error("Unexpected value in PgClass::typeName()");
}
diff --git a/pglablib/catalog/PgClass.h b/pglablib/catalog/PgClass.h
index 4e53a93..524b87d 100644
--- a/pglablib/catalog/PgClass.h
+++ b/pglablib/catalog/PgClass.h
@@ -22,7 +22,9 @@ enum class RelKind {
MaterializedView, // m
Composite, // c
Toast, // t
- ForeignTable // f
+ ForeignTable, // f
+ PartitionedTable, // p
+ PartitionedIndex // I
};
void operator<<(RelKind &s, const Pgsql::Value &v);
@@ -47,6 +49,7 @@ public:
int minmxid;
std::vector options;
QString viewdef;
+ QString partitionBoundaries;
using PgNamespaceObject::PgNamespaceObject;
diff --git a/pglablib/catalog/PgClassContainer.cpp b/pglablib/catalog/PgClassContainer.cpp
index eb563f6..426b390 100644
--- a/pglablib/catalog/PgClassContainer.cpp
+++ b/pglablib/catalog/PgClassContainer.cpp
@@ -16,6 +16,8 @@ std::string PgClassContainer::getLoadQuery() const
if (lessThenVersion(120000))
q += ", relhasoids ";
+ if (minimumVersion(100000))
+ q += ", pg_get_expr(relpartbound, oid)"; // partition specification
q +=
"\nFROM pg_catalog.pg_class \n"
@@ -53,5 +55,8 @@ PgClass PgClassContainer::loadElem(const Pgsql::Row &row)
if (lessThenVersion(120000))
col >> v.hasoids;
- return v;
+ if (minimumVersion(100000))
+ col >> v.partitionBoundaries;
+
+ return v;
}
From 7a4d8f3410e9fb26f8db8a44e64cdf4fd2dc170e Mon Sep 17 00:00:00 2001
From: eelke
Date: Sun, 8 Jan 2023 11:20:19 +0100
Subject: [PATCH 36/64] Release note for $$ fix
---
releasenotes/notes/dollar-quoted-string-691768c7c3cd7a1c.yaml | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 releasenotes/notes/dollar-quoted-string-691768c7c3cd7a1c.yaml
diff --git a/releasenotes/notes/dollar-quoted-string-691768c7c3cd7a1c.yaml b/releasenotes/notes/dollar-quoted-string-691768c7c3cd7a1c.yaml
new file mode 100644
index 0000000..d79c4fc
--- /dev/null
+++ b/releasenotes/notes/dollar-quoted-string-691768c7c3cd7a1c.yaml
@@ -0,0 +1,4 @@
+---
+fixes:
+ - |
+ Tokenizer now accepts $$ as the start of a dollar quoted string.
From 60fb4ce28507f6c44a04da59687eb9b464397475 Mon Sep 17 00:00:00 2001
From: eelke
Date: Wed, 18 Jan 2023 19:43:12 +0100
Subject: [PATCH 37/64] Improve support for declarative partitioning.
Generated SQL for a partition is now correct (atleast for simple cases)
Switched to C++ 20 so the ranges library can be used in this case
to filter unwanted items.
---
common.pri | 2 +-
pglablib/catalog/PgClass.cpp | 122 ++++++++++++++++++++++----------
pglablib/catalog/PgClass.h | 2 +
pglablib/catalog/PgConstraint.h | 4 ++
4 files changed, 92 insertions(+), 38 deletions(-)
diff --git a/common.pri b/common.pri
index cf53398..dcff145 100644
--- a/common.pri
+++ b/common.pri
@@ -4,7 +4,7 @@ error( "Use local.pri.sample to create your own local.pri" )
LIBS += -lUser32 -lws2_32 -llibpq
-CONFIG += c++latest
+CONFIG += c++20
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
diff --git a/pglablib/catalog/PgClass.cpp b/pglablib/catalog/PgClass.cpp
index 90b1b71..ccb007c 100644
--- a/pglablib/catalog/PgClass.cpp
+++ b/pglablib/catalog/PgClass.cpp
@@ -6,7 +6,7 @@
#include "PgInheritsContainer.h"
#include
#include "SqlFormattingUtils.h"
-
+#include
void operator<<(RelPersistence &s, const Pgsql::Value &v)
@@ -125,52 +125,90 @@ QString PgClass::createTableSql() const
sql += "TEMP ";
sql += "TABLE ";
sql += fullyQualifiedQuotedObjectName();
- sql += " (\n ";
- auto && cols = catalog().attributes()->getColumnsForRelation(oid());
- bool first = true;
- for (auto && col : cols)
+ if (!partitionBoundaries.isEmpty())
{
- if (col.num > 0 && !col.isdropped)
- {
- if (first)
- {
- first = false;
- }
- else
- sql += ",\n ";
- if (!col.islocal) sql += "-- ";
- sql += col.columnDefinition(catalog());
- }
-// { column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
-// | table_constraint
-// ] )
- }
- auto && constraints = catalog().constraints()->getConstraintsForRelation(oid());
- for (auto && constraint: constraints)
- {
- if (first)
- {
- sql += "\n ";
- first = false;
- }
- else
- sql += ",\n ";
- sql += getConstraintDefinition(catalog(), constraint, " ");
- }
+ sql += " PARTITION OF " + getPartitionOfName();
+ sql += generateBodySql(true);
+ }
+ else
+ sql += generateBodySql(false);
- sql += "\n)"
- % generateInheritsSql()
- // [ PARTITION BY { RANGE | LIST } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
- // [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
- // [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
+ sql += generateInheritsSql()
% 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());
@@ -189,6 +227,16 @@ QString PgClass::generateInheritsSql() const
return sql;
}
+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)
diff --git a/pglablib/catalog/PgClass.h b/pglablib/catalog/PgClass.h
index 524b87d..0591c37 100644
--- a/pglablib/catalog/PgClass.h
+++ b/pglablib/catalog/PgClass.h
@@ -62,7 +62,9 @@ private:
mutable QString createSqlCache;
QString createTableSql() const;
+ QString generateBodySql(bool isPartition) const;
QString generateInheritsSql() const;
+ QString getPartitionOfName() const;
QString generateTablespaceSql() const;
QString createViewSql() const;
};
diff --git a/pglablib/catalog/PgConstraint.h b/pglablib/catalog/PgConstraint.h
index 8a16a6b..ae8feac 100644
--- a/pglablib/catalog/PgConstraint.h
+++ b/pglablib/catalog/PgConstraint.h
@@ -79,6 +79,10 @@ public:
QString dropSql() const override;
QString createSql() const override;
+ bool isInherited() const
+ {
+ return inhcount > 0;
+ }
};
From ccd88d0578fbf083d8de600e42e94a5999ff3d96 Mon Sep 17 00:00:00 2001
From: eelke
Date: Sat, 21 Jan 2023 10:27:17 +0100
Subject: [PATCH 38/64] Show in constraint list and in generated SQL when a
constraint is inherited
---
pglab/catalog/models/ConstraintModel.cpp | 33 ++++++++++++-------
pglab/catalog/models/ConstraintModel.h | 1 +
.../catalog/widgets/CatalogConstraintPage.cpp | 26 ++++++++++-----
pglab/catalog/widgets/CatalogConstraintPage.h | 1 +
pglablib/catalog/PgConstraint.cpp | 11 +++++--
5 files changed, 50 insertions(+), 22 deletions(-)
diff --git a/pglab/catalog/models/ConstraintModel.cpp b/pglab/catalog/models/ConstraintModel.cpp
index 02b36ea..9933db5 100644
--- a/pglab/catalog/models/ConstraintModel.cpp
+++ b/pglab/catalog/models/ConstraintModel.cpp
@@ -55,6 +55,9 @@ QVariant ConstraintModel::headerData(int section, Qt::Orientation orientation, i
case SupportingIndexCol:
c = tr("Supporting index");
break;
+ case InheritedCol:
+ c = tr("Inherited");
+ break;
// case DefinitionCol:
// c = tr("Definition");
// break;
@@ -85,11 +88,17 @@ int ConstraintModel::columnCount(const QModelIndex &) const
// return v;
//}
-Oid ConstraintModel::getType(int ) const
+Oid ConstraintModel::getType(int col) const
{
- Oid oid = Pgsql::varchar_oid;
-
- return oid;
+ Oid oid;
+ switch (col) {
+ case InheritedCol:
+ oid = Pgsql::bool_oid;
+ break;
+ default:
+ oid = Pgsql::varchar_oid;
+ }
+ return oid;
}
QString IconForConstraintType(ConstraintType ct)
@@ -128,25 +137,27 @@ QVariant ConstraintModel::getData(const QModelIndex &index) const
const auto &t = m_constraints[row];
const int col = index.column();
- QString s;
- switch (col) {
+
+ switch (col) {
case TypeCol:
- s = IconForConstraintType(t.type);
+ v = IconForConstraintType(t.type);
break;
case NameCol:
- s = t.objectName();
+ v = t.objectName();
break;
case NsCol:
- s = t.nsName();
+ v = t.nsName();
break;
case SupportingIndexCol:
- s = getIndexDisplayString(*m_catalog, t.indid);
+ v = getIndexDisplayString(*m_catalog, t.indid);
break;
+ case InheritedCol:
+ v = t.isInherited();
+ break;
// case DefinitionCol:
// s = t.definition;
// break;
}
- v = s;
return v;
}
diff --git a/pglab/catalog/models/ConstraintModel.h b/pglab/catalog/models/ConstraintModel.h
index 29b4bcf..42dea1b 100644
--- a/pglab/catalog/models/ConstraintModel.h
+++ b/pglab/catalog/models/ConstraintModel.h
@@ -19,6 +19,7 @@ public:
NameCol, ///
NsCol, ///
SupportingIndexCol,
+ InheritedCol,
// DefinitionCol,
colCount };
diff --git a/pglab/catalog/widgets/CatalogConstraintPage.cpp b/pglab/catalog/widgets/CatalogConstraintPage.cpp
index a910c57..6140e64 100644
--- a/pglab/catalog/widgets/CatalogConstraintPage.cpp
+++ b/pglab/catalog/widgets/CatalogConstraintPage.cpp
@@ -34,13 +34,21 @@ void CatalogConstraintPage::setFilter(const std::optional &cls)
void CatalogConstraintPage::tableView_selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/)
{
- auto rijen = selectedRows();
- QString drops;
- QString creates;
- for (auto rij : rijen) {
- const PgConstraint constraint = m_constraintModel->constraint(rij);
- drops += constraint.dropSql() % "\n";
- creates += constraint.createSql() % "\n";
- }
- m_definitionView->setPlainText(drops % "\n" % creates);
+ m_definitionView->setPlainText(
+ generateSql(
+ selectedRows()
+ )
+ );
+}
+
+QString CatalogConstraintPage::generateSql(const std::unordered_set &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;
}
diff --git a/pglab/catalog/widgets/CatalogConstraintPage.h b/pglab/catalog/widgets/CatalogConstraintPage.h
index ea8c212..43e53b9 100644
--- a/pglab/catalog/widgets/CatalogConstraintPage.h
+++ b/pglab/catalog/widgets/CatalogConstraintPage.h
@@ -22,6 +22,7 @@ private:
ConstraintModel *m_constraintModel = nullptr;
+ QString generateSql(const std::unordered_set &rows);
private slots:
void tableView_selectionChanged(const QItemSelection &selected, const QItemSelection &deselected);
};
diff --git a/pglablib/catalog/PgConstraint.cpp b/pglablib/catalog/PgConstraint.cpp
index 9c06487..83ad036 100644
--- a/pglablib/catalog/PgConstraint.cpp
+++ b/pglablib/catalog/PgConstraint.cpp
@@ -1,5 +1,6 @@
#include "PgConstraint.h"
#include "SqlFormattingUtils.h"
+#include
void operator<<(ConstraintType &s, const Pgsql::Value &v)
{
@@ -176,10 +177,16 @@ QString PgConstraint::typeName() const
QString PgConstraint::dropSql() const
{
- return getDropConstraintDefinition(catalog(), *this);
+ if (isInherited())
+ return "-- " % objectName() % " is inherited";
+
+ return getDropConstraintDefinition(catalog(), *this);
}
QString PgConstraint::createSql() const
{
- return getAlterTableConstraintDefinition(catalog(), *this);
+ if (isInherited())
+ return "-- " % objectName() % " is inherited";
+
+ return getAlterTableConstraintDefinition(catalog(), *this);
}
From 2ff9577d4126e47389c104aca52d7000a40f2552 Mon Sep 17 00:00:00 2001
From: Eelke Klein
Date: Tue, 24 Jan 2023 17:47:52 +0000
Subject: [PATCH 39/64] The table inheritance works mostly
---
pglab/catalog/models/DatabasesTableModel.h | 4 +-
.../{models => tables}/TablesTableModel.cpp | 175 +++++++++++-------
.../{models => tables}/TablesTableModel.h | 63 ++++---
pglab/catalog/widgets/CatalogTablesPage.cpp | 24 ++-
pglab/catalog/widgets/CatalogTablesPage.h | 6 +-
pglab/pglab.pro | 4 +-
pglab/serverinspector/DatabasesPage.cpp | 6 +-
pglab/serverinspector/RolesPage.cpp | 4 +-
pglab/util/PgLabItemDelegate.cpp | 2 +-
pglab/util/PgLabTableViewHelper.h | 54 ++++--
pglablib/catalog/PgInheritsContainer.h | 13 +-
pglablib/pglablib.pro | 6 +
pglablib/ui/catalog/tables/TableNode.cpp | 20 ++
pglablib/ui/catalog/tables/TableNode.h | 25 +++
pglablib/ui/catalog/tables/TableSize.cpp | 6 +
pglablib/ui/catalog/tables/TableSize.h | 13 ++
.../ui/catalog/tables/TableTreeBuilder.cpp | 82 ++++++++
pglablib/ui/catalog/tables/TableTreeBuilder.h | 31 ++++
pgsql/Pgsql_ResultConstIterator.h | 8 +-
.../notes/tabletree-efb590181a614167.yaml | 7 +
tests/pglabtests/TableTreeBuilderTests.cpp | 64 +++++++
tests/pglabtests/pglabtests.pro | 1 +
22 files changed, 473 insertions(+), 145 deletions(-)
rename pglab/catalog/{models => tables}/TablesTableModel.cpp (57%)
rename pglab/catalog/{models => tables}/TablesTableModel.h (54%)
create mode 100644 pglablib/ui/catalog/tables/TableNode.cpp
create mode 100644 pglablib/ui/catalog/tables/TableNode.h
create mode 100644 pglablib/ui/catalog/tables/TableSize.cpp
create mode 100644 pglablib/ui/catalog/tables/TableSize.h
create mode 100644 pglablib/ui/catalog/tables/TableTreeBuilder.cpp
create mode 100644 pglablib/ui/catalog/tables/TableTreeBuilder.h
create mode 100644 releasenotes/notes/tabletree-efb590181a614167.yaml
create mode 100644 tests/pglabtests/TableTreeBuilderTests.cpp
diff --git a/pglab/catalog/models/DatabasesTableModel.h b/pglab/catalog/models/DatabasesTableModel.h
index a8ecc8e..8e46aaa 100644
--- a/pglab/catalog/models/DatabasesTableModel.h
+++ b/pglab/catalog/models/DatabasesTableModel.h
@@ -38,9 +38,9 @@ public:
virtual Oid getType(int column) const override;
virtual QVariant getData(const QModelIndex &index) const override;
- RowItem rowItem(int row) const
+ RowItem rowItem(const QModelIndex &index) const
{
- return databases.at(row).database;
+ return databases.at(index.row()).database;
}
protected:
virtual QVariant getDataMeaning(const QModelIndex &index) const override;
diff --git a/pglab/catalog/models/TablesTableModel.cpp b/pglab/catalog/tables/TablesTableModel.cpp
similarity index 57%
rename from pglab/catalog/models/TablesTableModel.cpp
rename to pglab/catalog/tables/TablesTableModel.cpp
index fd285ed..2d49c75 100644
--- a/pglab/catalog/models/TablesTableModel.cpp
+++ b/pglab/catalog/tables/TablesTableModel.cpp
@@ -5,15 +5,16 @@
#include "catalog/PgClass.h"
#include "catalog/PgClassContainer.h"
#include "catalog/PgNamespace.h"
-#include "catalog/PgNamespaceContainer.h"
-#include "Pgsql_declare.h"
+#include "catalog/PgInheritsContainer.h"
+//#include "Pgsql_declare.h"
+#include "ui/catalog/tables/TableTreeBuilder.h"
#include "CustomDataRole.h"
#include
#include
#include "Pgsql_Connection.h"
TablesTableModel::TablesTableModel(std::shared_ptr opendatabase, QObject *parent)
- : QAbstractTableModel(parent)
+ : QAbstractItemModel(parent)
, openDatabase(opendatabase)
{}
@@ -36,17 +37,18 @@ void TablesTableModel::setCatalog(std::shared_ptr cat)
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;
+ case RelKind::Table:
+ case RelKind::View:
+ case RelKind::MaterializedView:
+ case RelKind::ForeignTable:
+ case RelKind::PartitionedTable:
+ return true;
+ default:
+ return false;
}
}
+
void TablesTableModel::refresh()
{
beginResetModel();
@@ -55,31 +57,19 @@ void TablesTableModel::refresh()
if (!m_catalog)
return;
- // Later afscheiden naar filter functie
auto classes = m_catalog->classes();
+ rootNode.reset();
+
+ auto nsfilter = GetNamespaceFilterLambda();
+ std::map 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 oidIndex;
- m_tables.clear();
- for (const auto &e : *classes) {
- bool add = false;
- if (TableLike(e.kind)) {
- 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));
}
@@ -106,14 +96,46 @@ QVariant TablesTableModel::headerData(int section, Qt::Orientation orientation,
return QVariant();
}
-int TablesTableModel::rowCount(const QModelIndex &) const
+int TablesTableModel::rowCount(const QModelIndex &parent) const
{
- return static_cast(m_tables.size());
+ if (parent.isValid())
+ {
+ const TableNode *parentItem = static_cast(parent.internalPointer());
+ return static_cast(parentItem->children.size());
+ }
+ if (rootNode != nullptr)
+ return static_cast(rootNode->children.size());
+
+ return 0;
}
int TablesTableModel::columnCount(const QModelIndex &) const
{
- return colCount;
+ return colCount;
+}
+
+QModelIndex TablesTableModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (parent.isValid())
+ {
+ const TableNode *parentItem = static_cast(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(index.internalPointer());
+ auto parent = item->parent.lock();
+ if (parent && parent != rootNode)
+ {
+ return createIndex(parent->myIndex, 0, parent.get());
+ }
+ }
+ return {};
}
Oid TablesTableModel::getType(int column) const
@@ -142,37 +164,29 @@ Oid TablesTableModel::getType(int column) const
QVariant TablesTableModel::getData(const QModelIndex &index) const
{
- const auto &table = m_tables[index.row()];
- const auto &t = table._class;
- const auto &s = table.sizes;
+// const auto &table = rootNode->children[index.row()];
+ const TableNode* table = nodeFromIndex(index);
+ const auto &t = table->_class;
+ const auto &s = table->sizes;
+
switch (index.column()) {
- case NameCol: return t.objectName();
- case NamespaceCol: return t.nsName();
- case KindCol: return t.typeName();
- case OwnerCol: return t.ownerName();
- case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace);
- case OptionsCol: break;
- case AclCol: return t.aclString();
- case CommentCol: return t.description;
- case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
- case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
- case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
- case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
+ case NameCol: return t.objectName();
+ case NamespaceCol: return t.nsName();
+ case KindCol: return t.typeName();
+ case OwnerCol: return t.ownerName();
+ case TablespaceCol: return getTablespaceDisplayString(*m_catalog, t.tablespace);
+ case OptionsCol: break;
+ case AclCol: return t.aclString();
+ case CommentCol: return t.description;
+ case TotalSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes;
+ case TableSizeCol: return s.oid == 0 ? QVariant() : s.totalBytes - s.indexBytes - s.toastBytes;
+ case IndexSizeCol: return s.oid == 0 ? QVariant() : s.indexBytes;
+ case ToastSizeCol: return s.oid == 0 ? QVariant() : s.toastBytes;
}
return QVariant();
}
-PgClass TablesTableModel::getTable(int row) const
-{
- return m_tables[row]._class;
-}
-
-Oid TablesTableModel::getTableOid(int row) const
-{
- return m_tables[row]._class.oid();
-}
-
QVariant TablesTableModel::data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
@@ -195,7 +209,7 @@ QVariant TablesTableModel::data(const QModelIndex &index, int role) const
return QVariant();
}
-void TablesTableModel::StartLoadTableSizes(std::map oidIndex)
+void TablesTableModel::StartLoadTableSizes(OidClassIndex oidIndex)
{
QPointer p(this);
QtConcurrent::run([this]
@@ -205,7 +219,7 @@ void TablesTableModel::StartLoadTableSizes(std::map oidIndex)
.then(qApp, [p, oidIndex] (TableSizes sizes)
{
if (p)
- p.data()->PopulateSizes(std::move(oidIndex), std::move(sizes));
+ p.data()->PopulateSizes(oidIndex, sizes);
});
}
@@ -254,18 +268,41 @@ TablesTableModel::TableSizes TablesTableModel::QueryTableSizes() const
return sizes;
}
-void TablesTableModel::PopulateSizes(std::map oidIndex, std::vector sizes)
+void TablesTableModel::PopulateSizes(
+ const OidClassIndex &oidIndex,
+ const std::vector &sizes
+)
{
for (auto s : sizes)
{
auto findIter = oidIndex.find(s.oid);
if (findIter != oidIndex.end())
{
- m_tables[findIter->second].sizes = s;
+ findIter->second->sizes = s;
}
}
emit dataChanged(
- createIndex(0, TotalSizeCol),
- createIndex(static_cast(m_tables.size()), ToastSizeCol)
- );
+ index(0, TotalSizeCol),
+ index(static_cast(rootNode->children.size()), ToastSizeCol)
+ );
+}
+
+const TableNode* TablesTableModel::nodeFromIndex(const QModelIndex &index)
+{
+ return static_cast(index.internalPointer());
+}
+
+std::function 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"; };
+ }
+
}
diff --git a/pglab/catalog/models/TablesTableModel.h b/pglab/catalog/tables/TablesTableModel.h
similarity index 54%
rename from pglab/catalog/models/TablesTableModel.h
rename to pglab/catalog/tables/TablesTableModel.h
index 880dcd8..2e02aa5 100644
--- a/pglab/catalog/models/TablesTableModel.h
+++ b/pglab/catalog/tables/TablesTableModel.h
@@ -1,9 +1,12 @@
-#ifndef TABLESTABLEMODEL_H
+#ifndef TABLESTABLEMODEL_H
#define TABLESTABLEMODEL_H
-#include "BaseTableModel.h"
+//#include "catalog/models/BaseTableModel.h"
+#include "ui/catalog/tables/TableNode.h"
+#include "ui/catalog/tables/TableSize.h"
#include "NamespaceFilter.h"
#include "catalog/PgClass.h"
+#include
#include
#include
@@ -11,7 +14,7 @@ class OpenDatabase;
class PgClass;
class PgDatabaseCatalog;
-class TablesTableModel: public QAbstractTableModel {
+class TablesTableModel: public QAbstractItemModel {
public:
using RowItem = PgClass;
enum e_Columns : int {
@@ -40,42 +43,31 @@ public:
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
- virtual QVariant data(const QModelIndex &index, int role) const override;
- PgClass getTable(int row) const;
- RowItem rowItem(int row) const
+ QModelIndex index(int row, int column,
+ const QModelIndex &parent = QModelIndex()) const override;
+
+ QModelIndex parent(const QModelIndex &index) const override;
+
+ QVariant data(const QModelIndex &index, int role) const override;
+
+ RowItem rowItem(const QModelIndex &index) const
{
- return getTable(row);
+ return nodeFromIndex(index)->_class;
}
- Oid getTableOid(int row) const;
+
+ static const TableNode* nodeFromIndex(const QModelIndex &index);
private:
- class TableSize {
- public:
- int oid;
- int64_t totalBytes = -1;
- int64_t indexBytes = -1;
- int64_t toastBytes = -1;
- TableSize()
- : oid(0)
- {}
- };
using TableSizes = std::vector;
- class Table {
- public:
- PgClass _class;
- TableSize sizes;
-
- Table(const PgClass &cls)
- : _class(cls)
- {}
- };
- using t_Tables = std::vector;
+ // this is a readonly model so when the vectors have been filled
+ // they will not change anymore and it is safe to assume the indexes
+ // of elements do not change
std::shared_ptr m_catalog;
NamespaceFilter m_namespaceFilter = NamespaceFilter::User;
- t_Tables m_tables;
+ std::shared_ptr rootNode;
QMetaObject::Connection refreshConnection;
std::shared_ptr openDatabase;
@@ -83,10 +75,17 @@ private:
Oid getType(int column) const;
QVariant getData(const QModelIndex &index) const;
- void StartLoadTableSizes(std::map oidIndex);
- TableSizes QueryTableSizes() const;
- void PopulateSizes(std::map oidIndex, std::vector sizes);
+ using OidClassIndex = std::map>;
+ void StartLoadTableSizes(OidClassIndex oidIndex);
+ TableSizes QueryTableSizes() const;
+ void PopulateSizes(
+ const OidClassIndex &oidIndex,
+ const std::vector &sizes
+ );
+
+
+ std::function GetNamespaceFilterLambda();
private slots:
void refresh();
diff --git a/pglab/catalog/widgets/CatalogTablesPage.cpp b/pglab/catalog/widgets/CatalogTablesPage.cpp
index 8782545..d2d03f5 100644
--- a/pglab/catalog/widgets/CatalogTablesPage.cpp
+++ b/pglab/catalog/widgets/CatalogTablesPage.cpp
@@ -8,7 +8,7 @@
#include "catalog/widgets/TriggerPage.h"
#include "catalog/models/ColumnTableModel.h"
#include "catalog/models/ConstraintModel.h"
-#include "catalog/models/TablesTableModel.h"
+#include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableView.h"
#include "ResultTableModelUtil.h"
#include "widgets/SqlCodePreview.h"
@@ -27,7 +27,7 @@ CatalogTablesPage::CatalogTablesPage(std::shared_ptr opendatabase,
: QSplitter(Qt::Horizontal, parent)
, m_tablesTableView(this, new TablesTableModel(opendatabase, this))
{
- auto tv = m_tablesTableView.tableView();
+ auto tv = m_tablesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this);
@@ -106,7 +106,7 @@ void CatalogTablesPage::setCatalog(std::shared_ptr 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 ¤t, const QModelIndex &previous)
@@ -114,8 +114,8 @@ void CatalogTablesPage::tableListTable_currentRowChanged(const QModelIndex &curr
if (current.row() == previous.row())
return;
- auto table = m_tablesTableView.rowItemForProxyIndex(current);
- selectedTableChanged(table);
+ auto tableNode = TablesTableModel::nodeFromIndex(m_tablesTableView.sortFilter()->mapToSource(current)); // m_tablesTableView.rowItemForProxyIndex(current);
+ selectedTableChanged(tableNode->_class);
}
@@ -127,11 +127,15 @@ void CatalogTablesPage::tableListTable_layoutChanged(const QListmapToSource(index).row();
- PgClass table = m_tablesTableView.dataModel()->getTable(row);
- if (table.oid() != InvalidOid) {
- tableSelected(table.oid());
- }
+ auto sourceIndex = m_tablesTableView.sortFilter()->mapToSource(index);
+ auto tableNode = TablesTableModel::nodeFromIndex(sourceIndex);
+ if (tableNode)
+ {
+ Oid oid = tableNode->_class.oid();
+ if (oid != InvalidOid) {
+ tableSelected(oid);
+ }
+ }
}
void CatalogTablesPage::selectedTableChanged(const std::optional &table)
diff --git a/pglab/catalog/widgets/CatalogTablesPage.h b/pglab/catalog/widgets/CatalogTablesPage.h
index 02f61a5..01daf4b 100644
--- a/pglab/catalog/widgets/CatalogTablesPage.h
+++ b/pglab/catalog/widgets/CatalogTablesPage.h
@@ -2,13 +2,13 @@
#define CATALOGTABLESPAGE_H
#include "NamespaceFilter.h"
-#include "catalog/models/TablesTableModel.h"
+#include "catalog/tables/TablesTableModel.h"
#include "util/PgLabTableViewHelper.h"
#include
#include
#include
#include
-#include "Pgsql_oids.h"
+
class CatalogConstraintPage;
class CatalogIndexPage;
@@ -39,7 +39,7 @@ public:
signals:
void tableSelected(Oid tableoid);
private:
- PgLabTableViewHelper m_tablesTableView;
+ PgLabTableViewHelper m_tablesTableView;
// Details
QTabWidget *m_detailsTabs = nullptr;
diff --git a/pglab/pglab.pro b/pglab/pglab.pro
index 2c24c63..2ff93a5 100644
--- a/pglab/pglab.pro
+++ b/pglab/pglab.pro
@@ -32,8 +32,8 @@ SOURCES += main.cpp\
catalog/models/ProcTableModel.cpp \
catalog/models/RolesTableModel.cpp \
catalog/models/SequenceModel.cpp \
- catalog/models/TablesTableModel.cpp \
catalog/models/TriggerTableModel.cpp \
+ catalog/tables/TablesTableModel.cpp \
catalog/widgets/CatalogConstraintPage.cpp \
catalog/widgets/CatalogFunctionsPage.cpp \
catalog/widgets/CatalogIndexPage.cpp \
@@ -104,8 +104,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 \
diff --git a/pglab/serverinspector/DatabasesPage.cpp b/pglab/serverinspector/DatabasesPage.cpp
index 60f1aa6..0e3fc0a 100644
--- a/pglab/serverinspector/DatabasesPage.cpp
+++ b/pglab/serverinspector/DatabasesPage.cpp
@@ -12,7 +12,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr opendatabase, QWidget
: QSplitter(Qt::Horizontal, parent)
, m_databasesTableView(this, new DatabasesTableModel(opendatabase, this))
{
- auto tv = m_databasesTableView.tableView();
+ auto tv = m_databasesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
tv->setContextMenuPolicy(Qt::ActionsContextMenu);
@@ -23,7 +23,7 @@ DatabasesPage::DatabasesPage(std::shared_ptr opendatabase, QWidget
m_tableSql = new SqlCodePreview(this);
addWidget(m_tableSql);
- connect(m_databasesTableView.tableView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
+ connect(m_databasesTableView.itemView()->selectionModel(), &QItemSelectionModel::currentRowChanged,
this, &DatabasesPage::databaseSelectionChanged);
connect(createDbAction, &QAction::triggered,
[this](auto checked)
@@ -37,7 +37,7 @@ void DatabasesPage::setCatalog(std::shared_ptr cat)
{
m_catalog = cat;
m_databasesTableView.dataModel()->setDatabaseList(cat);
- m_databasesTableView.tableView()->resizeColumnsToContents();
+ m_databasesTableView.itemView()->resizeColumnsToContents();
}
void DatabasesPage::updateDatabaseDetails(const PgDatabase &db)
diff --git a/pglab/serverinspector/RolesPage.cpp b/pglab/serverinspector/RolesPage.cpp
index 529350a..6ea7a3a 100644
--- a/pglab/serverinspector/RolesPage.cpp
+++ b/pglab/serverinspector/RolesPage.cpp
@@ -6,7 +6,7 @@ RolesPage::RolesPage(QWidget * parent)
: QSplitter(Qt::Horizontal, parent)
, m_rolesTableView(this)
{
- auto tv = m_rolesTableView.tableView();
+ auto tv = m_rolesTableView.itemView();
tv->setSelectionMode(QAbstractItemView::SingleSelection);
m_detailsTabs = new QTabWidget(this);
@@ -19,5 +19,5 @@ void RolesPage::setCatalog(std::shared_ptr cat)
{
m_catalog = cat;
m_rolesTableView.dataModel()->setRoleList(cat->authIds());
- m_rolesTableView.tableView()->resizeColumnsToContents();
+ m_rolesTableView.itemView()->resizeColumnsToContents();
}
diff --git a/pglab/util/PgLabItemDelegate.cpp b/pglab/util/PgLabItemDelegate.cpp
index ae9e23c..db958c5 100644
--- a/pglab/util/PgLabItemDelegate.cpp
+++ b/pglab/util/PgLabItemDelegate.cpp
@@ -135,7 +135,7 @@ void PgLabItemDelegate::initStyleOption(QStyleOptionViewItem *option,
suffix = "B";
forground_color = QColorConstants::Svg::darkblue;
}
- option->text = QString{ "%1 %2" }.arg(val, 3, 'g', -1 ).arg(suffix) ;
+ option->text = QString{ "%1 %2" }.arg(val, 0, 'g', 3).arg(suffix) ;
}
else {
auto str = value.toString();
diff --git a/pglab/util/PgLabTableViewHelper.h b/pglab/util/PgLabTableViewHelper.h
index 67b5e55..e8c2363 100644
--- a/pglab/util/PgLabTableViewHelper.h
+++ b/pglab/util/PgLabTableViewHelper.h
@@ -1,25 +1,51 @@
#pragma once
#include
+#include
#include
+#include "util/PgLabItemDelegate.h"
#include "util/PgLabTableView.h"
#include
class PgDatabaseCatalog;
-template
+template
+void ResizeColumnsToContent(ViewType *vt)
+{
+ vt->resizeColumnsToContents();
+}
+
+template <>
+inline void ResizeColumnsToContent(QTreeView *)
+{
+}
+
+template
+void InitView(ViewType *vt)
+{}
+
+template <>
+inline void InitView(QTreeView *tv)
+{
+ tv->setAlternatingRowColors(true);
+ tv->setItemDelegate(new PgLabItemDelegate(tv));
+ tv->setWordWrap(false);
+}
+
+
+template
class PgLabTableViewHelper {
public:
PgLabTableViewHelper(QWidget * parent, TableModel *tableModel)
{
- m_tableView = new PgLabTableView(parent);
-
+ m_itemView = new ViewType(parent);
+ InitView(m_itemView);
m_dataModel = tableModel;
m_sortFilter = new QSortFilterProxyModel(parent);
m_sortFilter->setSourceModel(m_dataModel);
- m_tableView->setModel(m_sortFilter);
- m_tableView->setSortingEnabled(true);
+ m_itemView->setModel(m_sortFilter);
+ m_itemView->setSortingEnabled(true);
m_sortFilter->sort(0, Qt::AscendingOrder);
}
@@ -27,9 +53,9 @@ public:
: PgLabTableViewHelper(parent, new TableModel(parent))
{}
- PgLabTableView *tableView() const
+ ViewType *itemView() const
{
- return m_tableView;
+ return m_itemView;
}
TableModel *dataModel() const
@@ -45,27 +71,27 @@ public:
void setCatalog(std::shared_ptr cat)
{
m_dataModel->setCatalog(cat);
- m_tableView->resizeColumnsToContents();
+ ResizeColumnsToContent(m_itemView);
}
QModelIndex currentSourceIndex() const
{
- QModelIndex index = m_tableView->selectionModel()->currentIndex();
+ QModelIndex index = m_itemView->selectionModel()->currentIndex();
if (!index.isValid())
return index;
return m_sortFilter->mapToSource(index);
}
- typename TableModel::RowItem rowItemForSourceRow(int row) const
+ typename TableModel::RowItem rowItemForSourceRow(QModelIndex index) const
{
- return m_dataModel->rowItem(row);
+ return m_dataModel->rowItem(index);
}
typename TableModel::RowItem rowItemForProxyIndex(QModelIndex index)
{
QModelIndex sourceIndex = m_sortFilter->mapToSource(index);
- return m_dataModel->rowItem(sourceIndex.row());
+ return m_dataModel->rowItem(sourceIndex);
}
typename TableModel::RowItem rowItemForProxyRow(int row) const
@@ -79,12 +105,12 @@ public:
if (!index.isValid())
return {};
- return rowItemForSourceRow(index.row());
+ return rowItemForSourceRow(index);
}
private:
- PgLabTableView *m_tableView = nullptr;
+ ViewType *m_itemView = nullptr;
TableModel *m_dataModel = nullptr;
QSortFilterProxyModel *m_sortFilter = nullptr;
};
diff --git a/pglablib/catalog/PgInheritsContainer.h b/pglablib/catalog/PgInheritsContainer.h
index 3d95377..b94640c 100644
--- a/pglablib/catalog/PgInheritsContainer.h
+++ b/pglablib/catalog/PgInheritsContainer.h
@@ -4,17 +4,24 @@
#include "PgContainer.h"
#include "PgInherits.h"
-#include "Pgsql_declare.h"
+class IFindParents
+{
+public:
+ virtual std::vector getParentsOf(Oid oid) const = 0;
+};
-class PgInheritsContainer : public PgContainer {
+class PgInheritsContainer
+ : public PgContainer
+ , public IFindParents
+{
public:
using PgContainer::PgContainer;
virtual std::string getLoadQuery() const override;
/// Returns the parents in the correct order
- std::vector getParentsOf(Oid oid) const;
+ std::vector getParentsOf(Oid oid) const override;
protected:
PgInherits loadElem(const Pgsql::Row &row) override;
};
diff --git a/pglablib/pglablib.pro b/pglablib/pglablib.pro
index a835755..876bd84 100644
--- a/pglablib/pglablib.pro
+++ b/pglablib/pglablib.pro
@@ -45,6 +45,9 @@ SOURCES += \
catalog/PgConstraintContainer.cpp \
ParamListJson.cpp \
ParamListModel.cpp \
+ ui/catalog/tables/TableNode.cpp \
+ ui/catalog/tables/TableSize.cpp \
+ ui/catalog/tables/TableTreeBuilder.cpp \
util.cpp \
SqlFormattingUtils.cpp \
catalog/PgKeywordList.cpp \
@@ -113,6 +116,9 @@ HEADERS += \
catalog/PgConstraintContainer.h \
ParamListJson.h \
ParamListModel.h \
+ ui/catalog/tables/TableNode.h \
+ ui/catalog/tables/TableSize.h \
+ ui/catalog/tables/TableTreeBuilder.h \
util.h \
SqlFormattingUtils.h \
catalog/PgCatalogTypes.h \
diff --git a/pglablib/ui/catalog/tables/TableNode.cpp b/pglablib/ui/catalog/tables/TableNode.cpp
new file mode 100644
index 0000000..ec89c07
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableNode.cpp
@@ -0,0 +1,20 @@
+#include "TableNode.h"
+#include "catalog/PgDatabaseCatalog.h"
+namespace {
+ PgDatabaseCatalog dummyCatalog;
+}
+
+TableNode::TableNode()
+: _class(dummyCatalog, InvalidOid, "", InvalidOid)
+{
+
+}
+
+TableNode::TableNode(const PgClass &cls)
+: _class(cls)
+{}
+
+const TableNode *TableNode::getChildPtr(int index) const
+{
+ return children.at(index).get();
+}
diff --git a/pglablib/ui/catalog/tables/TableNode.h b/pglablib/ui/catalog/tables/TableNode.h
new file mode 100644
index 0000000..6e462b6
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableNode.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "TableSize.h"
+#include "catalog/PgClass.h"
+
+class TableNode;
+using t_Tables = std::vector>;
+
+
+
+
+class TableNode {
+public:
+ PgClass _class;
+ TableSize sizes;
+
+ int myIndex;
+ std::weak_ptr parent; // hope we do not need it (must be weak to prevent circular reference)
+ t_Tables children;
+
+ TableNode();
+ TableNode(const PgClass &cls);
+
+ const TableNode* getChildPtr(int index) const;
+};
diff --git a/pglablib/ui/catalog/tables/TableSize.cpp b/pglablib/ui/catalog/tables/TableSize.cpp
new file mode 100644
index 0000000..e411142
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableSize.cpp
@@ -0,0 +1,6 @@
+#include "TableSize.h"
+
+
+TableSize::TableSize()
+: oid(0)
+{}
diff --git a/pglablib/ui/catalog/tables/TableSize.h b/pglablib/ui/catalog/tables/TableSize.h
new file mode 100644
index 0000000..56da2c5
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableSize.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include
+
+class TableSize {
+public:
+ int oid;
+ int64_t totalBytes = -1;
+ int64_t indexBytes = -1;
+ int64_t toastBytes = -1;
+
+ TableSize();
+};
diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.cpp b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp
new file mode 100644
index 0000000..44a3269
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableTreeBuilder.cpp
@@ -0,0 +1,82 @@
+#include "TableTreeBuilder.h"
+#include "catalog/PgInheritsContainer.h"
+#include "TableNode.h"
+#include
+
+TableTreeBuilder::TableTreeBuilder(
+ const std::map &source,
+ const IFindParents &inheritance
+)
+: source(source)
+, inheritance(inheritance)
+{
+}
+
+std::tuple, std::map>> TableTreeBuilder::Build()
+{
+ rootNode = std::make_shared();
+
+ // when childrens are ordered before there parents
+ // the parent will automatically be added first (recursively)
+ // processed nodes are tracked in the nodes member
+ for (auto && e : source | std::views::filter(
+ [this] (auto &e)
+ {
+ return processedNodes.count(e.first) == 0;
+ })
+ )
+ {
+ addNode(e.second);
+ }
+ AssignNodeIndexesAndParents(rootNode);
+ return { rootNode, std::move(processedNodes) };
+}
+
+
+std::shared_ptr TableTreeBuilder::addNode(const PgClass &cls)
+{
+ std::shared_ptr node = std::make_shared(cls);
+ processedNodes.emplace(cls.oid(), node);
+ std::vector parents = inheritance.getParentsOf(cls.oid());
+ if (parents.empty())
+ addToToplevel(node);
+ else
+ addToParents(node, parents);
+ return node;
+}
+
+void TableTreeBuilder::addToToplevel(std::shared_ptr node)
+{
+ rootNode->children.push_back(node);
+}
+
+void TableTreeBuilder::addToParents(std::shared_ptr node, const std::vector &parents)
+{
+ for (auto &parent : parents)
+ {
+ getParent(parent)->children.push_back(node);
+ }
+}
+
+std::shared_ptr TableTreeBuilder::getParent(Oid oid)
+{
+ auto parent = processedNodes.find(oid);
+ if (parent != processedNodes.end())
+ return parent->second;
+
+ // Not present, find in source list and add now
+ auto source_iter = source.find(oid);
+ assert(source_iter != source.end());
+ return addNode(source_iter->second);
+}
+
+void TableTreeBuilder::AssignNodeIndexesAndParents(std::shared_ptr parent)
+{
+ int index = 0;
+ for (auto &n : parent->children)
+ {
+ n->parent = parent;
+ n->myIndex = index++;
+ AssignNodeIndexesAndParents(n);
+ }
+}
diff --git a/pglablib/ui/catalog/tables/TableTreeBuilder.h b/pglablib/ui/catalog/tables/TableTreeBuilder.h
new file mode 100644
index 0000000..99ccdb1
--- /dev/null
+++ b/pglablib/ui/catalog/tables/TableTreeBuilder.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "Pgsql_oids.h"
+#include