diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 30cf57e..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,10 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Ignored default folder with query files
-/queries/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/QtSettings.xml b/.idea/QtSettings.xml
deleted file mode 100644
index 0248d0c..0000000
--- a/.idea/QtSettings.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
deleted file mode 100644
index a3aeb4c..0000000
--- a/.idea/editor.xml
+++ /dev/null
@@ -1,367 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
deleted file mode 100644
index df87cf9..0000000
--- a/.idea/encodings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
deleted file mode 100644
index 0b76fe5..0000000
--- a/.idea/misc.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index 498ae80..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/pgLab.iml b/.idea/pgLab.iml
deleted file mode 100644
index 4c94235..0000000
--- a/.idea/pgLab.iml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.junie/skills/reno-release-notes/SKILL.md b/.junie/skills/reno-release-notes/SKILL.md
deleted file mode 100644
index 2a1779f..0000000
--- a/.junie/skills/reno-release-notes/SKILL.md
+++ /dev/null
@@ -1,110 +0,0 @@
----
-name: reno-release-notes
-description: Create, review and lint pgLab release notes with reno (releasenotes/notes YAML fragments). Use whenever the user mentions reno, release notes, releasenotes/notes, "reno new", "reno lint", or asks to add or check a note for a user-visible change in pgLab, even if reno is not named explicitly.
----
-
-# reno release notes in pgLab
-
-pgLab uses [reno](https://docs.openstack.org/reno/latest/) for release notes.
-Each user-visible change adds its own small YAML file in `releasenotes/notes/`,
-committed together with the change. reno derives the release from git tags, so
-never move or rename notes to change releases. Content is reStructuredText, not
-Markdown.
-
-The notes are rendered in the Sphinx user docs through the
-`.. release-notes::` directive in `docs/releasenotes.rst` (docs build:
-`pip install -r docs/requirements.txt && sphinx-build -b html docs userdocs`).
-The About dialog links to the generated `releasenotes.html`.
-
-## When to write a note
-
-Write one for: new features, behaviour changes users will notice (UI, menus,
-shortcuts, query/code generation output, connection handling), bug fixes,
-known issues, and upgrade impact (e.g. changed stored settings or connection
-config format).
-
-Skip: pure refactors, build-system/CI changes, test-only changes, internal
-code cleanup, docs-only edits.
-
-## Sections configured for this project
-
-Defined in `releasenotes/config.yaml` (render order). Only these keys are valid;
-others (`deprecations`, `critical`) are NOT configured here and will not render.
-
-| Key | Rendered title | Use for |
-|------------|------------------|--------------------------------------------|
-| `prelude` | (implicit) | Release-wide summary, single string |
-| `features` | New Features | New functionality |
-| `other` | Changes | Changes to existing behaviour, minor items |
-| `issues` | Known Issues | Known problems / limitations |
-| `upgrade` | Upgrade Notes | What users must do or know when upgrading |
-| `security` | Security Issues | Security fixes |
-| `fixes` | Bug Fixes | Ordinary bug fixes |
-
-Every section except `prelude` is a list of strings. Encoding is utf8.
-
-## Creating a note
-
-From the repo root (`pip install reno` if missing):
-
-```bash
-reno new short-descriptive-slug
-```
-
-This creates `releasenotes/notes/short-descriptive-slug-<16 hex>.yaml`. Keep the
-random suffix. Existing examples: `f5-bindings-…`,
-`connection-details-in-window-title-…`, `dollar-quoted-string-…`.
-Delete all unused template sections, keep only what you need.
-
-```yaml
----
-features:
- - |
- The query tool now executes the current statement with
- :kbd:`F5`.
-fixes:
- - |
- Fixed reconnect dialog that kept popping up after the connection was lost.
-```
-
-Short single-line items may be plain (`- text`), longer ones use `- |`.
-Wrap at roughly 80 columns.
-
-## Writing style
-
-- Write for pgLab users, not patch reviewers: describe the visible effect, not
- the implementation (no class or function names unless users see them).
-- Past or present tense, full sentences, one bullet per distinct change.
-- RST markup: inline literals with double backticks (``` ``pg_catalog`` ```),
- links as `` `text `_ ``, GUI menu paths with
- `:menuselection:` (e.g. `:menuselection:`Help --> About``), keys with
- `:kbd:`. No Markdown, no single-backtick code.
-- Mention the PostgreSQL feature or object type involved when relevant.
-- For `upgrade`, state the action required.
-
-## Validate before committing
-
-```bash
-reno lint
-reno list # note must be tracked by git to appear for tagged history
-```
-
-Common failures: YAML indentation/tabs, misspelled section key, a string where
-a list is required (or a list for `prelude`), an unconfigured section, bad RST.
-Optionally build the docs (command above) to check rendering.
-
-## Commit rules
-
-- `git add releasenotes/notes/` and commit it in the same commit as the
- change, not a follow-up.
-- Commit messages are plain imperative sentences (e.g. "Fix reconnect dialog
- popping up repeatedly.").
-- To correct a note, edit the same file; don't add a second one.
-
-## Troubleshooting
-
-- Note missing in report: not committed, or shallow clone without tags
- (`git fetch --tags --unshallow`).
-- Wrong release: decided by git history/tags; check `reno list`.
-- Broken rendering: almost always RST (single backticks, Markdown links,
- indentation).
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index b9c08b0..0000000
--- a/AGENTS.md
+++ /dev/null
@@ -1,32 +0,0 @@
-# AGENTS.md
-
-pgLab: Qt 6 / C++20 desktop GUI for PostgreSQL.
-
-## Layout (link order: core, pgsql -> pglablib -> pglab)
-- `core/` – generic utilities (SQL lexer, CSV writer, `Expected`, JSON); no DB access.
-- `pgsql/` – thin C++ wrapper around libpq (connection, result, params, oids).
-- `pglablib/` – catalog models, code builders, query generation, connection config.
-- `pglab/` – the Qt Widgets application (executable).
-- `tests/` – gtest executables `PgsqlTests` and `pglabtests`, plus `mygtestutils` (Qt `PrintTo` helpers).
-- `docs/` – Sphinx user docs; `releasenotes/notes` – `reno` notes.
-
-## Build and test
-CMake + vcpkg is the active build system (`VCPKG_ROOT` must be set). Qt 6 is not provided by vcpkg; it must be installed on the system.
-```
-cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
-cmake --build build
-ctest --test-dir build --output-on-failure
-```
-- Run one suite: `./build/tests/pglabtests/pglabtests --gtest_filter='SqlLexer*'` (same for `PgsqlTests`).
-- `-DBUILD_TESTS=OFF` skips tests. `build/` is git-ignored; the first configure builds dependencies and is slow.
-- Run the app headless with `QT_QPA_PLATFORM=offscreen ./build/pglab/pglab`. It needs a PostgreSQL server for anything useful.
-
-## Gotchas
-- Tests are registered per executable, so each `ctest` entry covers a whole suite.
-- A new test file must be added to the `add_executable` list in `tests//CMakeLists.txt`. Sources are listed explicitly, with no globbing. The same goes for new sources in each library's `CMakeLists.txt`.
-- `core/test/` is not referenced by any CMake file (its tests are built from `tests/pglabtests`). Don't expect changes there to be compiled.
-- `AUTOMOC`/`AUTOUIC`/`AUTORCC` are on globally, so `.ui` and `.qrc` files only need to be listed as sources.
-- `BOOST_ENABLE_ASSERT_HANDLER` is defined; the handler lives in `core/my_boost_assert_handler.cpp`.
-- Commit messages are plain imperative sentences. User-visible changes need a reno note (`releasenotes/notes`).
-- Docs build: `pip install -r docs/requirements.txt && sphinx-build -b html docs userdocs`.
-- Windows (VS 2022) is also a supported platform. Avoid Linux-only code, and don't break `WIN32_LEAN_AND_MEAN`/`NOMINMAX` assumptions.
diff --git a/CMakeLists.txt b/CMakeLists.txt
deleted file mode 100644
index a172074..0000000
--- a/CMakeLists.txt
+++ /dev/null
@@ -1,30 +0,0 @@
-cmake_minimum_required(VERSION 3.21)
-
-project(pgLab LANGUAGES C CXX)
-
-set(CMAKE_CXX_STANDARD 20)
-set(CMAKE_CXX_STANDARD_REQUIRED ON)
-set(CMAKE_CXX_EXTENSIONS OFF)
-
-set(CMAKE_AUTOMOC ON)
-set(CMAKE_AUTOUIC ON)
-set(CMAKE_AUTORCC ON)
-
-add_compile_definitions(BOOST_ENABLE_ASSERT_HANDLER QT_DEPRECATED_WARNINGS)
-if(WIN32)
- add_compile_definitions(WIN32_LEAN_AND_MEAN NOMINMAX)
-endif()
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Sql Concurrent)
-
-option(BUILD_TESTS "Build the gtest executables and register them with ctest" ON)
-
-add_subdirectory(core)
-add_subdirectory(pgsql)
-add_subdirectory(pglablib)
-add_subdirectory(pglab)
-
-if(BUILD_TESTS)
- enable_testing()
- add_subdirectory(tests)
-endif()
diff --git a/README.md b/README.md
index 7c2eb7e..bdf5ea0 100644
--- a/README.md
+++ b/README.md
@@ -11,41 +11,3 @@ The release notes use collected from git using [reno](https://docs.openstack.org
- googletest
- libpq
-Boost, Botan, libpq and googletest are provided by vcpkg through the manifest in `vcpkg.json`, so you do not install them manually.
-
-## Required tools
-
-- CMake 3.21 or newer
-- A C++20 compiler (GCC or Clang on Linux, Visual Studio 2022 on Windows)
-- Qt 6 with the Core, Gui, Widgets, Sql and Concurrent components
-- vcpkg, with the `VCPKG_ROOT` environment variable pointing to its checkout
-- Ninja or make (optional, for faster builds)
-- A PostgreSQL server (only needed to run the application against a database)
-
-## Building
-
-Configure with the vcpkg toolchain file. The first configure downloads and builds the dependencies, which takes a while:
-
-```
-cmake -S . -B build -DCMAKE_TOOLCHAIN_FILE=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
-cmake --build build
-```
-
-To skip the test executables, add `-DBUILD_TESTS=OFF` to the configure command.
-
-## Running the tests
-
-From the build directory, run:
-
-```
-ctest --output-on-failure
-```
-
-## Running the application
-
-The executable is built to `build/pglab/pglab`. On a machine without a display, set `QT_QPA_PLATFORM=offscreen`:
-
-```
-./build/pglab/pglab
-```
-
diff --git a/common.pri b/common.pri
new file mode 100644
index 0000000..dcff145
--- /dev/null
+++ b/common.pri
@@ -0,0 +1,15 @@
+! include( ./local.pri ) {
+error( "Use local.pri.sample to create your own local.pri" )
+}
+
+LIBS += -lUser32 -lws2_32 -llibpq
+
+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
+# depend on your compiler). Please consult the documentation of the
+# deprecated API in order to know how to port your code away from it.
+DEFINES += QT_DEPRECATED_WARNINGS
+DEFINES += BOOST_ENABLE_ASSERT_HANDLER
+DEFINES += WIN32_LEAN_AND_MEAN NOMINMAX _WIN32_WINNT=0x0501
diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt
index ec63cf3..95b09fc 100644
--- a/core/CMakeLists.txt
+++ b/core/CMakeLists.txt
@@ -1,31 +1,41 @@
+set(CMAKE_AUTOMOC ON)
+
add_library(core STATIC
- my_boost_assert_handler.cpp
- SqlLexer.cpp
- CsvWriter.cpp
BackupFormatModel.cpp
- ExplainTreeModelItem.cpp
- jsoncpp.cpp
- SqlLexer.h
- ScopeGuard.h
- CsvWriter.h
- BackupFormatModel.h
- Expected.h
- ExplainTreeModelItem.h
- json/json.h
- std_utils.h
- IntegerRange.h
- )
-
+ CsvWriter.cpp
+ my_boost_assert_handler.cpp
+ PasswordManager.cpp
+ SqlLexer.cpp)
+
target_link_libraries(core PUBLIC
- Qt6::Core
+ boost
+ botan
+ Qt5::Core
)
-
-target_include_directories(core PUBLIC
+
+target_include_directories(core INTERFACE
$
+)
+
+set_target_properties(core PROPERTIES
+ CXX_STANDARD 14
+ CXX_STANDARD_REQUIRED ON
+ POSITION_INDEPENDENT_CODE True
)
-set_target_properties(core PROPERTIES
- CXX_STANDARD 20
- CXX_STANDARD_REQUIRED ON
- POSITION_INDEPENDENT_CODE ON
+add_executable(runtests
+ test/main.cpp
+ test/tst_CsvWriter.cpp
+ test/tst_expected.cpp
+ test/tst_PasswordManager.cpp
+ test/tst_scopeguard.cpp
+ test/tst_SqlLexer.cpp
)
+
+target_link_libraries(runtests
+ core
+ gtest
+ Threads::Threads
+ )
+
+add_test(tests runtests)
diff --git a/core/core.pro b/core/core.pro
new file mode 100644
index 0000000..0b424f6
--- /dev/null
+++ b/core/core.pro
@@ -0,0 +1,43 @@
+#-------------------------------------------------
+#
+# Project created by QtCreator 2017-02-26T10:51:14
+#
+#-------------------------------------------------
+
+QT -= gui
+
+TARGET = core
+TEMPLATE = lib
+CONFIG += staticlib
+
+! include( ../common.pri ) {
+error( "Couldn't find the common.pri file!" )
+}
+
+# You can also make your code fail to compile if you use deprecated APIs.
+# In order to do so, uncomment the following line.
+# You can also select to disable deprecated APIs only up to a certain version of Qt.
+#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
+
+SOURCES += my_boost_assert_handler.cpp \
+ SqlLexer.cpp \
+ CsvWriter.cpp \
+ BackupFormatModel.cpp \
+ ExplainTreeModelItem.cpp \
+ jsoncpp.cpp
+
+HEADERS += \
+ SqlLexer.h \
+ ScopeGuard.h \
+ CsvWriter.h \
+ BackupFormatModel.h \
+ Expected.h \
+ ExplainTreeModelItem.h \
+ json/json.h \
+ std_utils.h \
+ IntegerRange.h
+
+unix {
+ target.path = /usr/lib
+ INSTALLS += target
+}
diff --git a/local.pri.sample b/local.pri.sample
new file mode 100644
index 0000000..44b8f1e
--- /dev/null
+++ b/local.pri.sample
@@ -0,0 +1,17 @@
+# Need paths here to find following library headers
+# - botan
+# - boost
+# - libpq
+# ???
+INCLUDEPATH += c:/prog/include
+INCLUDEPATH += c:/prog/include/pgsql
+INCLUDEPATH += c:/VSproj/boost/include/boost-1_68
+
+# Need paths here to find library files for
+# - botan
+# - boost
+# - libpq
+#
+LIBS += -L\VSproj\boost\lib -L/PROG/LIB
+
+GOOGLETEST_DIR = c:/prog/googletest
diff --git a/pglab/CMakeLists.txt b/pglab/CMakeLists.txt
index fb3d3f2..d6a1072 100644
--- a/pglab/CMakeLists.txt
+++ b/pglab/CMakeLists.txt
@@ -1,115 +1,69 @@
-set(PGLAB_SOURCES
- main.cpp
- About.cpp
- catalog/delegates/IconColumnDelegate.cpp
- catalog/models/BaseTableModel.cpp
- catalog/models/ColumnTableModel.cpp
- catalog/models/ConstraintModel.cpp
- catalog/models/DatabasesTableModel.cpp
- catalog/models/DependantsTableModel.cpp
- catalog/models/IndexModel.cpp
- catalog/models/ProcTableModel.cpp
- catalog/models/RolesTableModel.cpp
- catalog/models/SequenceModel.cpp
- catalog/models/TriggerTableModel.cpp
- catalog/tables/TablesTableModel.cpp
- catalog/widgets/CatalogConstraintPage.cpp
- catalog/widgets/CatalogFunctionsPage.cpp
- catalog/widgets/CatalogIndexPage.cpp
- catalog/widgets/CatalogInspector.cpp
- catalog/widgets/CatalogNamespacePage.cpp
- catalog/widgets/CatalogPageBase.cpp
- catalog/widgets/CatalogSequencesPage.cpp
- catalog/widgets/CatalogTablesPage.cpp
- catalog/widgets/CatalogTypesPage.cpp
- catalog/widgets/ColumnPage.cpp
- catalog/widgets/DependantsPage.cpp
- catalog/widgets/TriggerPage.cpp
- codeeditor/CodeEditor.cpp
- codeeditor/EditorGutter.cpp
- codeeditor/GutterPainter.cpp
- crud/CrudModel.cpp
- crud/CrudTab.cpp
- crud/DataGridWidget.cpp
- querytool/QueryExplainModel.cpp
- querytool/QueryParamListController.cpp
- querytool/QueryTool.cpp
- serverinspector/DatabasesPage.cpp
- serverinspector/RolesPage.cpp
- serverinspector/ServerInspector.cpp
- util/Colors.cpp
- util/PgLabItemDelegate.cpp
- util/PgLabTableView.cpp
- util/SqlSyntaxHighlighter.cpp
- ConnectionConfigurationWidget.cpp
- ConnectionController.cpp
- ManagedPage.cpp
- PgDumpOutputHighlighter.cpp
- CreateDatabaseDialog.cpp
- ConnectionManagerWindow.cpp
- ConnectionListModel.cpp
- SslModeModel.cpp
- stopwatch.cpp
- tuplesresultwidget.cpp
- BackupDialog.cpp
- MasterController.cpp
- ParamTypeDelegate.cpp
- OpenDatabase.cpp
- ProcessStdioWidget.cpp
- ResultTableModelUtil.cpp
- NamespaceItemModel.cpp
- PropertyProxyModel.cpp
- CodeGenerator.cpp
- UserConfiguration.cpp
- widgets/SqlCodePreview.cpp
- CustomFilterSortModel.cpp
- PropertiesPage.cpp
- PasswordPromptDialog.cpp
- EditTableWidget.cpp
- EditColumnTableModel.cpp
- DatabaseWindow.cpp
- widgets/SingleRecordModel.cpp
- widgets/SingleRecordWidget.cpp
- )
+cmake_minimum_required(VERSION 3.2)
+project(pglab)
-set(PGLAB_FORMS
- ConnectionManagerWindow.ui
- CreateDatabaseDialog.ui
- DatabaseWindow.ui
- tuplesresultwidget.ui
- querytool/QueryTab.ui
- ProcessStdioWidget.ui
- crud/CrudTab.ui
- CodeGenerator.ui
- widgets/SingleRecordWidget.ui
- )
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTOUIC ON)
+set(CMAKE_AUTORCC ON)
add_executable(pglab
- ${PGLAB_SOURCES}
- ${PGLAB_FORMS}
+ ASyncDBConnection.cpp
+ ASyncWindow.cpp
+ BackupDialog.cpp
+ BackupRestore.cpp
+ CodeBuilderConfiguration.cpp
+ ConnectionConfig.cpp
+ ConnectionList.cpp
+ ConnectionListModel.cpp
+ ConnectionManagerWindow.cpp
+ DatabaseInspectorWidget.cpp
+ DatabasesTableModel.cpp
+ DatabaseWindow.cpp
+ ExplainTreeModelItem.cpp
+ GlobalIoService.cpp
+ jsoncpp.cpp
+ main.cpp
+ MainWindow.cpp
+ MasterController.cpp
+ OpenDatabase.cpp
+ ParamListModel.cpp
+ ParamTypeDelegate.cpp
+ PgAuthIdContainer.cpp
+ PgAuthId.cpp
+ PgClass.cpp
+ PgDatabaseCatalogue.cpp
+ PgDatabaseContainer.cpp
+ PgDatabase.cpp
+ PgNamespace.cpp
+ PgTypeContainer.cpp
+ PgType.cpp
+ ProcessStdioWidget.cpp
+ QueryExplainModel.cpp
+ QueryResultModel.cpp
+ QueryTab.cpp
+ RolesTableModel.cpp
+ ServerWindow.cpp
+ SqlSyntaxHighlighter.cpp
+ stopwatch.cpp
+ tsqueue.cpp
+ tuplesresultwidget.cpp
+ TypeSelectionItemModel.cpp
+ util.cpp
resources.qrc
)
-if(WIN32)
- set_target_properties(pglab PROPERTIES WIN32_EXECUTABLE ON)
+set_target_properties(pglab PROPERTIES
+ CXX_STANDARD 14
+ CXX_STANDARD_REQUIRED ON)
+
+if(CMAKE_COMPILER_IS_GNUCXX)
+ # target_compile_options(pglab PRIVATE -Wall -fpic -march=native )
+ # set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og")
endif()
-target_link_libraries(pglab PRIVATE
- pglablib
+target_link_libraries( pglab
+ core
pgsql
- core
- Qt6::Core
- Qt6::Gui
- Qt6::Widgets
- Qt6::Sql
- Qt6::Concurrent
- )
-
-target_include_directories(pglab PRIVATE
- ${CMAKE_CURRENT_SOURCE_DIR}
- )
-
-set_target_properties(pglab PROPERTIES
- CXX_STANDARD 20
- CXX_STANDARD_REQUIRED ON
+ boost-system
+ Qt5::Widgets
+ pthread
)
diff --git a/pglab/CreateDatabaseDialog.h b/pglab/CreateDatabaseDialog.h
index 74cb209..bcc00af 100644
--- a/pglab/CreateDatabaseDialog.h
+++ b/pglab/CreateDatabaseDialog.h
@@ -3,7 +3,7 @@
#include
-#include "catalog/PgDatabase.h"
+#include "catalog\PgDatabase.h"
class PgDatabaseCatalog;
diff --git a/pglab/CreateDatabaseDialog.ui b/pglab/CreateDatabaseDialog.ui
index a16e077..f17dbac 100644
--- a/pglab/CreateDatabaseDialog.ui
+++ b/pglab/CreateDatabaseDialog.ui
@@ -148,7 +148,7 @@
CodeEditor
QPlainTextEdit
-
+
diff --git a/pglab/catalog/models/DatabasesTableModel.cpp b/pglab/catalog/models/DatabasesTableModel.cpp
index 289eea2..ac10ec5 100644
--- a/pglab/catalog/models/DatabasesTableModel.cpp
+++ b/pglab/catalog/models/DatabasesTableModel.cpp
@@ -169,7 +169,7 @@ QVariant DatabasesTableModel::getData(const QModelIndex &index) const
break;
case SizeCol:
if (d.size.totalBytes >= 0)
- v = QVariant::fromValue(d.size.totalBytes);
+ v = d.size.totalBytes;
break;
case AclCol:
v = db.aclString();
diff --git a/pglab/catalog/models/IndexModel.cpp b/pglab/catalog/models/IndexModel.cpp
index e04f715..7cee4e4 100644
--- a/pglab/catalog/models/IndexModel.cpp
+++ b/pglab/catalog/models/IndexModel.cpp
@@ -114,7 +114,7 @@ QVariant IndexModel::getData(const QModelIndex &index) const
break;
case SizeCol:
- v = QVariant::fromValue(dat.sizeBytes);
+ v = dat.sizeBytes;
break;
case ExplicitIndexCol:
v = !dat.isSupportingIndex();
diff --git a/pglab/catalog/models/SequenceModel.cpp b/pglab/catalog/models/SequenceModel.cpp
index 640b50c..659348f 100644
--- a/pglab/catalog/models/SequenceModel.cpp
+++ b/pglab/catalog/models/SequenceModel.cpp
@@ -102,12 +102,12 @@ QVariant SequenceModel::data(const QModelIndex &index, int role) const
case NameCol: return seq.objectName();
case SchemaCol: return seq.nsName();
case OwnerCol: return seq.ownerName();
- case LastCol: return QVariant::fromValue(seq.last);
- case StartCol: return QVariant::fromValue(seq.start);
- case MinCol: return QVariant::fromValue(seq.min);
- case MaxCol: return QVariant::fromValue(seq.max);
- case IncrementCol: return QVariant::fromValue(seq.increment);
- case CacheCol: return QVariant::fromValue(seq.cache);
+ case LastCol: return seq.last;
+ case StartCol: return seq.start;
+ case MinCol: return seq.min;
+ case MaxCol: return seq.max;
+ case IncrementCol: return seq.increment;
+ case CacheCol: return seq.cache;
case CycledCol: return seq.cycled;
case CalledCol: return seq.called;
case AclCol: return seq.aclString();
diff --git a/pglab/catalog/tables/TablesTableModel.cpp b/pglab/catalog/tables/TablesTableModel.cpp
index d6a4e8b..0df8ff2 100644
--- a/pglab/catalog/tables/TablesTableModel.cpp
+++ b/pglab/catalog/tables/TablesTableModel.cpp
@@ -178,10 +178,10 @@ QVariant TablesTableModel::getData(const QModelIndex &index) const
case OptionsCol: break;
case AclCol: return t.aclString();
case CommentCol: return t.description;
- case TotalSizeCol: return s.oid == 0 ? QVariant() : QVariant::fromValue(s.totalBytes);
- case TableSizeCol: return s.oid == 0 ? QVariant() : QVariant::fromValue(s.totalBytes - s.indexBytes - s.toastBytes);
- case IndexSizeCol: return s.oid == 0 ? QVariant() : QVariant::fromValue(s.indexBytes);
- case ToastSizeCol: return s.oid == 0 ? QVariant() : QVariant::fromValue(s.toastBytes);
+ 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();
diff --git a/pglab/crud/CrudModel.cpp b/pglab/crud/CrudModel.cpp
index c30a1bc..d688bd6 100644
--- a/pglab/crud/CrudModel.cpp
+++ b/pglab/crud/CrudModel.cpp
@@ -4,13 +4,11 @@
#include "catalog/PgAttribute.h"
#include "catalog/PgAttributeContainer.h"
#include "catalog/PgConstraintContainer.h"
-#include "catalog/PgClassContainer.h"
#include "SqlFormattingUtils.h"
#include "Pgsql_oids.h"
#include "CustomDataRole.h"
#include
#include
-#include
#include
#include "Pgsql_oids.h"
#include "Pgsql_PgException.h"
@@ -41,23 +39,12 @@ void CrudModel::setConfig(std::shared_ptr db, const PgClass &table
{
m_database = db;
m_table = table;
- m_wholeTable = true;
+ m_primaryKey = db->catalog()->constraints()->getPrimaryForRelation(table.oid());
callLoadData = true;
auto dbconfig = m_database->config();
m_dbConn.setupConnection(dbconfig);
}
-void CrudModel::setResult(std::shared_ptr db, std::shared_ptr result)
-{
- m_database = db;
- m_table.reset();
- m_wholeTable = false;
- callLoadData = false;
- auto dbconfig = m_database->config();
- m_dbConn.setupConnection(dbconfig);
- loadIntoModel(result);
-}
-
QVariant CrudModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (section == 0)
@@ -163,7 +150,7 @@ QVariant StringToVariant(const std::string &str, Oid oid)
case Pgsql::oid_oid:
case Pgsql::int8_oid:
- return QVariant::fromValue(static_cast(v));
+ return v.operator int64_t();
case Pgsql::bool_oid:
return v.operator bool();
@@ -218,8 +205,6 @@ QVariant CrudModel::data(const QModelIndex &index, int role) const
if (value)
return StringToVariant(value->c_str(), typ);
}
- else if (role == Qt::ForegroundRole && !m_wholeTable && columnIsReadOnly(index.column()))
- return QBrush(Qt::gray);
return {};
}
@@ -244,18 +229,13 @@ void CrudModel::loadData()
void CrudModel::loadIntoModel(std::shared_ptr data)
{
beginResetModel();
- bool was_dirty = hasPendingChanges();
m_pendingRowList.clear();
m_roData = data;
initializeColumnList();
- m_mapping.emplace(createResultSourceMapping(*m_roData, *m_database->catalog()));
lastRowKey = data->rows() - 1;
initRowMapping();
- if (m_wholeTable)
- appendNewRowInternal();
+ appendNewRowInternal();
endResetModel();
- if (was_dirty)
- emit pendingChangesChanged(false);
}
void CrudModel::initRowMapping()
@@ -297,7 +277,7 @@ Qt::ItemFlags CrudModel::flags(const QModelIndex &index) const
if (index.column() < PreColumnCount)
return flags;
- if (!columnIsReadOnly(index.column()))
+ if (m_primaryKey && !columnIsReadOnly(index.column()))
flags |= Qt::ItemIsEditable;
return flags;
@@ -324,10 +304,7 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
s.clear();
val = s;
}
- bool was_dirty = hasPendingChanges();
m_pendingRowList.setValue(col, row_mapping.rowKey, val);
- if (!was_dirty)
- emit pendingChangesChanged(true);
emit dataChanged(index, index, QVector() << role);
return true;
@@ -335,17 +312,22 @@ bool CrudModel::setData(const QModelIndex &index, const QVariant &value, int rol
return false;
}
-std::tuple> CrudModel::saveRow(const PendingRow &pending_row, const RowMapping &row_mapping)
+std::tuple> CrudModel::saveRow(const PendingRow &pending_row)
{
auto data = pending_row.data();
- if (data.empty())
- return { true, {} };
+ RowMapping& rowmapping = m_rowMapping[pending_row.row()];
- if (row_mapping.isNew())
+ if (!data.empty())
{
QString buffer;
Pgsql::Params params;
- std::tie(buffer, params) = createInsertQuery(pending_row);
+ if (rowmapping.isNew())
+ std::tie(buffer, params) = createInsertQuery(pending_row);
+ else
+ {
+ Pgsql::Params pkey_params = getPKeyParamsForRow(pending_row.row());
+ std::tie(buffer, params) = createUpdateQuery(pkey_params, pending_row);
+ }
Pgsql::Connection db_update_conn;
auto dbconfig = m_database->config();
@@ -374,120 +356,59 @@ std::tuple> CrudModel::saveRow(const Pending
msgBox.setText(ex.what());
msgBox.exec();
}
- return { false, {} };
- }
-
- return saveUpdate(pending_row, row_mapping);
-}
-
-std::tuple> CrudModel::saveUpdate(const PendingRow &pending_row, const RowMapping &row_mapping)
-{
- std::set tables;
- for (const auto &e : pending_row.data())
- tables.insert(m_mapping->tableOid(e.first));
-
- try
- {
- Pgsql::Connection db_update_conn;
- auto dbconfig = m_database->config();
- db_update_conn.connect(dbconfig.connectionString());
- auto tx = Pgsql::Transaction::startTransaction(db_update_conn);
- for (Oid table_oid : tables)
- {
- QString buffer;
- Pgsql::Params params;
- std::tie(buffer, params) = createUpdateQuery(table_oid, pending_row, row_mapping);
- auto result = tx.queryParam(buffer, params);
- if (!result || result.tuplesAffected() != 1)
- {
- QMessageBox::warning(nullptr, tr("Update failed"), tr("The change did not update exactly one row in %1, the row may have changed or is no longer unique.").arg(tableName(table_oid)));
- return { false, {} };
- }
- }
- tx.commit();
-
- std::vector values;
- values.reserve(m_roData->cols());
- for (int col = 0; col < m_roData->cols(); ++col)
- {
- auto &&pending = pending_row.data();
- auto found = pending.find(col);
- values.push_back(found != pending.end() ? found->second : getSavedData(row_mapping, col));
- }
- return { true, values };
- }
- catch (const Pgsql::PgResultError &ex)
- {
- QMessageBox msgBox;
- msgBox.setText(ex.what());
- msgBox.exec();
}
return { false, {} };
}
-QString CrudModel::tableName(Oid table_oid) const
+Pgsql::Params CrudModel::getPKeyParamsForRow(int row) const
{
- auto table = m_database->catalog()->classes()->getByKey(table_oid);
- return table ? table->fullyQualifiedQuotedObjectName() : QString();
-}
-
-std::vector CrudModel::resultColumnsOfTable(Oid table_oid) const
-{
- std::vector cols;
- for (int col = 0; col < m_roData->cols(); ++col)
- if (m_roData->ftable(col) == table_oid && m_roData->ftableCol(col) > 0)
- cols.push_back(col);
- return cols;
-}
-
-QString CrudModel::attributeName(int col) const
-{
- if (col < static_cast(columnList.size()) && columnList[col])
- return columnList[col]->name;
- return QString::fromUtf8(m_roData->getColName(col));
-}
-
-Pgsql::Params CrudModel::getPKeyParamsForRow(const RowMapping &row_mapping, Oid table_oid) const
-{
- Pgsql::Params params;
- for (int col : m_mapping->primaryKeyColumns(table_oid))
+ Pgsql::Params params;
+ for (auto attnum : m_primaryKey->key)
{
- auto s = getSavedData(row_mapping, col);
- params.add(s, getType(col));
- }
- return params;
+ const int col = attNumToCol(attnum);
+ Oid t = getType(col);
+ auto s = getSavedData(col, row);
+ params.add(s, t);
+ }
+ return params;
}
-std::tuple CrudModel::createUpdateQuery(Oid table_oid, const PendingRow &pending_row, const RowMapping &row_mapping)
+QString CrudModel::columnName(int col) const
{
- Pgsql::Params params;
- QString table_name = tableName(table_oid);
- QString buffer;
- QTextStream q(&buffer);
- q << "UPDATE " << table_name << " AS d\n SET ";
- int param = 0;
- for (auto& e : pending_row.data())
- {
- if (m_mapping->tableOid(e.first) != table_oid)
- continue;
- if (param > 0)
- q << ",";
- q << quoteIdent(attributeName(e.first)) << "=$" << ++param;
- params.add(e.second, getType(e.first));
- }
+ return m_roData->getColName(col);
+}
- q << "\nWHERE ";
- int i = 0;
- for (int col : m_mapping->primaryKeyColumns(table_oid))
+std::tuple CrudModel::createUpdateQuery(const Pgsql::Params &pkey_params, const PendingRow &pending_row)
+{
+ Pgsql::Params params;
+ auto data = pending_row.data();
+ QString table_name = m_table->fullyQualifiedQuotedObjectName();
+ QString buffer;
+ QTextStream q(&buffer);
+ q << "UPDATE " << table_name << " AS d\n SET ";
+ int param = 0;
+ for (auto& e : data)
{
- if (i > 0)
- q << " AND ";
- q << quoteIdent(attributeName(col)) << "=$" << ++param;
- ++i;
- }
- params.addParams(getPKeyParamsForRow(row_mapping, table_oid));
- q.flush();
- return { buffer, params };
+ if (param > 0)
+ q << ",";
+ q << quoteIdent(columnName(e.first)) << "=$" << ++param;
+ params.add(e.second, getType(e.first));
+ }
+
+ q << "\nWHERE ";
+ int i = 0;
+ for (auto attnum : m_primaryKey->key)
+ {
+ int col = attNumToCol(attnum);
+ if (i > 0)
+ q << " AND ";
+ q << quoteIdent(columnName(col)) << "=$" << ++param;
+ ++i;
+ }
+ params.addParams(pkey_params);
+ q << "\nRETURNING *";
+ q.flush();
+ return { buffer, params };
}
std::tuple CrudModel::createInsertQuery(const PendingRow &pending_row)
@@ -503,11 +424,12 @@ std::tuple CrudModel::createInsertQuery(const PendingRow
for (const auto& e : data)
{
int num = e.first;
+ auto&& column = columnList[num];
if (first)
first = false;
else
q << ",";
- q << quoteIdent(attributeName(num));
+ q << quoteIdent(column.name);
}
q << ") VALUES ($1";
for (size_t p = 2; p <= data.size(); ++p)
@@ -520,6 +442,19 @@ std::tuple CrudModel::createInsertQuery(const PendingRow
return { buffer, params };
}
+std::tuple CrudModel::createDeleteStatement(const PKeyValues &pkey_values)
+{
+ Pgsql::Params params;
+ size_t i = 0;
+ for (auto attnum : m_primaryKey->key)
+ {
+ const int col = attNumToCol(attnum);
+ params.add(pkey_values[i].c_str(), getType(col));
+ ++i;
+ }
+
+ return { createDeleteStatement(), params };
+}
QString CrudModel::createDeleteStatement() const
{
@@ -530,11 +465,12 @@ QString CrudModel::createDeleteStatement() const
q << "DELETE FROM " << table_name;
q << "\nWHERE ";
int i = 0;
- for (int col : m_mapping->primaryKeyColumns(m_table->oid()))
+ for (auto attnum : m_primaryKey->key)
{
+ const int col = attNumToCol(attnum);
if (i > 0)
q << " AND ";
- q << quoteIdent(attributeName(col)) << "=$" << ++i;
+ q << quoteIdent(columnName(col)) << "=$" << ++i;
}
q.flush();
@@ -547,13 +483,7 @@ bool CrudModel::savePendingChanges()
while (!m_pendingRowList.m_rows.empty())
{
auto iter = m_pendingRowList.m_rows.begin();
- int pendingKey = iter->first;
- auto mapping_iter = std::find_if(m_rowMapping.begin(), m_rowMapping.end(),
- [pendingKey](const RowMapping &rhs) -> bool { return rhs.rowKey == pendingKey; });
- if (mapping_iter == m_rowMapping.end())
- return false;
-
- auto [ok, modified_row] = saveRow(iter->second, *mapping_iter);
+ auto [ok, modified_row] = saveRow(iter->second);
if (ok)
{
int rowKey = iter->first;
@@ -586,13 +516,9 @@ bool CrudModel::columnIsReadOnly(int column_index) const
if (m_roData == nullptr)
return true;
- int col = column_index - PreColumnCount;
- if (!m_mapping || !m_mapping->isEditable(col))
- return true;
-
- const auto &column = columnList[col];
- return !column || column->getIdentity() == PgAttribute::Identity::Always
- || column->getGenerated() != PgAttribute::Generated::None;
+ auto&& column = columnList[column_index - PreColumnCount];
+ return column.getIdentity() == PgAttribute::Identity::Always
+ || column.getGenerated() != PgAttribute::Generated::None;
}
void CrudModel::initializeColumnList()
@@ -600,51 +526,28 @@ void CrudModel::initializeColumnList()
columnList.clear();
columnList.reserve(m_roData->cols());
auto atts = m_database->catalog()->attributes();
+ auto columns = atts->getColumnsForRelation(m_table->oid());
for (int col = 0; col < m_roData->cols(); ++col)
{
int attnum = m_roData->ftableCol(col);
- if (attnum <= 0)
- {
- columnList.emplace_back();
- continue;
- }
- auto columns = atts->getColumnsForRelation(m_roData->ftable(col));
auto find_result = std::find_if(columns.begin(), columns.end(), [attnum](const PgAttribute &att) { return att.num == attnum; });
- if (find_result != columns.end())
- columnList.emplace_back(*find_result);
- else
- columnList.emplace_back();
+ assert(find_result != columns.end());
+ columnList.push_back(*find_result);
}
}
-bool CrudModel::saveChanges()
+bool CrudModel::submit()
{
- bool was_dirty = hasPendingChanges();
- bool ok = savePendingChanges();
- if (was_dirty != hasPendingChanges())
- emit pendingChangesChanged(hasPendingChanges());
- return ok;
+ return savePendingChanges();
}
-void CrudModel::revertChanges()
+void CrudModel::revert()
{
- if (!hasPendingChanges())
- return;
- m_pendingRowList.clear();
- for (auto &row_mapping : m_rowMapping)
- row_mapping.pending = false;
-
- if (rowCount() > 0)
- emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1));
- emit pendingChangesChanged(false);
}
void CrudModel::appendNewRow()
{
- if (!m_wholeTable)
- return;
-
int row = static_cast(m_rowMapping.size());
beginInsertRows(QModelIndex(), row, row);
appendNewRowInternal();
@@ -662,8 +565,6 @@ std::tuple CrudModel::removeRows(const std::set
return { true, "" };
if (row_ranges.rbegin()->end() > static_cast(m_rowMapping.size()))
return { false, "Range error" };
- if (!m_wholeTable || !m_table || m_mapping->primaryKeyColumns(m_table->oid()).empty())
- return { false, "Rows can only be removed from a table with a primary key" };
// When removing rows there is no direct mapping anymore between the rows in the grid
// and the rows in m_roData
@@ -688,7 +589,7 @@ std::tuple CrudModel::removeRows(const std::set
for (int current_row = range.start(); current_row < range.end(); ++current_row)
{
auto&& mapping = m_rowMapping[static_cast(current_row)];
- auto params = getPKeyParamsForRow(mapping, m_table->oid());
+ auto params = getPKeyParamsForRow(mapping.rowKey);
if (!params.empty())
db_update_conn.queryParam(delete_statement, params);
}
@@ -697,10 +598,7 @@ std::tuple CrudModel::removeRows(const std::set
// If something goes wrong after this commit we should reload contents of model
}
// Then from model
- bool was_dirty = hasPendingChanges();
RemoveRangesOfRowsFromModel(row_ranges);
- if (was_dirty != hasPendingChanges())
- emit pendingChangesChanged(hasPendingChanges());
return { true, "" };
}
catch (const Pgsql::PgResultError &error)
@@ -735,8 +633,6 @@ void CrudModel::RemoveRangeOfRowsFromModel(IntegerRange range)
beginRemoveRows(QModelIndex(), range.start(), range.end() - 1);
SCOPE_EXIT { endRemoveRows(); };
auto first = m_rowMapping.begin() + range.start();
- for (auto iter = first; iter != first + range.length(); ++iter)
- m_pendingRowList.m_rows.erase(iter->rowKey);
m_rowMapping.erase(first, first + range.length());
}
diff --git a/pglab/crud/CrudModel.h b/pglab/crud/CrudModel.h
index 485b6c2..5a6beb3 100644
--- a/pglab/crud/CrudModel.h
+++ b/pglab/crud/CrudModel.h
@@ -5,10 +5,8 @@
#include "Pgsql_Connection.h"
#include "IntegerRange.h"
-#include "ResultSourceMapping.h"
#include "catalog/PgClass.h"
#include "catalog/PgConstraint.h"
-#include "catalog/PgAttribute.h"
#include
#include