From 49f6619a4e8b702a02a4d30d2a99d52fef13f68c Mon Sep 17 00:00:00 2001 From: eelke Date: Sat, 17 Oct 2026 16:21:22 +0200 Subject: [PATCH] Switch to cmake + vcpkg, builds on linux, windows needs verification. Code has been modified to be portable. --- CMakeLists.txt | 30 +++ README.md | 38 ++++ core/CMakeLists.txt | 56 +++--- pglab/CMakeLists.txt | 164 +++++++++------ pglab/CreateDatabaseDialog.h | 2 +- pglab/CreateDatabaseDialog.ui | 2 +- pglab/catalog/models/DatabasesTableModel.cpp | 2 +- pglab/catalog/models/IndexModel.cpp | 2 +- pglab/catalog/models/SequenceModel.cpp | 12 +- pglab/catalog/tables/TablesTableModel.cpp | 8 +- pglab/crud/CrudModel.cpp | 2 +- pglab/querytool/QueryExplainModel.cpp | 2 +- pglablib/ASyncDBConnection.cpp | 200 ++++++++++--------- pglablib/ASyncDBConnection.h | 3 +- pglablib/CMakeLists.txt | 187 +++++++++++++++++ pglablib/SqlFormattingUtils.cpp | 2 +- pglablib/WaitHandleList.cpp | 31 --- pglablib/WaitHandleList.h | 26 --- pglablib/Win32Event.h | 47 ----- pglablib/catalog/PgLanguage.cpp | 6 +- pglablib/catalog/PgProc.cpp | 10 +- pglablib/catalog/PgServerObject.cpp | 2 +- pglablib/catalog/PgServerObject.h | 6 +- pglablib/codebuilder/StringEscapeRule.h | 1 - pglablib/utils/HumanReadableBytes.h | 1 + pglablib/utils/PasswordManager.cpp | 6 +- pglablib/utils/PasswordManager.h | 6 +- pgsql/ArrayParser.cpp | 1 + pgsql/CMakeLists.txt | 48 +++++ tests/CMakeLists.txt | 5 + tests/PgsqlTests/CMakeLists.txt | 24 +++ tests/mygtestutils/CMakeLists.txt | 19 ++ tests/pglabtests/CMakeLists.txt | 41 ++++ vcpkg.json | 13 ++ 34 files changed, 678 insertions(+), 327 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 pglablib/CMakeLists.txt delete mode 100644 pglablib/WaitHandleList.cpp delete mode 100644 pglablib/WaitHandleList.h delete mode 100644 pglablib/Win32Event.h create mode 100644 pgsql/CMakeLists.txt create mode 100644 tests/CMakeLists.txt create mode 100644 tests/PgsqlTests/CMakeLists.txt create mode 100644 tests/mygtestutils/CMakeLists.txt create mode 100644 tests/pglabtests/CMakeLists.txt create mode 100644 vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a172074 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,30 @@ +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 bdf5ea0..7c2eb7e 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,41 @@ 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/core/CMakeLists.txt b/core/CMakeLists.txt index 95b09fc..ec63cf3 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -1,41 +1,31 @@ -set(CMAKE_AUTOMOC ON) - add_library(core STATIC - BackupFormatModel.cpp - CsvWriter.cpp my_boost_assert_handler.cpp - PasswordManager.cpp - SqlLexer.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 + ) + target_link_libraries(core PUBLIC - boost - botan - Qt5::Core + Qt6::Core ) - -target_include_directories(core INTERFACE + +target_include_directories(core PUBLIC $ -) + ) -set_target_properties(core PROPERTIES - CXX_STANDARD 14 +set_target_properties(core PROPERTIES + CXX_STANDARD 20 CXX_STANDARD_REQUIRED ON - POSITION_INDEPENDENT_CODE True + 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/pglab/CMakeLists.txt b/pglab/CMakeLists.txt index d6a1072..ab4dda5 100644 --- a/pglab/CMakeLists.txt +++ b/pglab/CMakeLists.txt @@ -1,69 +1,115 @@ -cmake_minimum_required(VERSION 3.2) -project(pglab) +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 + querytool/QueryExplainModel.cpp + querytool/QueryParamListController.cpp + querytool/QueryResultModel.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 + ) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) +set(PGLAB_FORMS + ConnectionManagerWindow.ui + CreateDatabaseDialog.ui + DatabaseWindow.ui + tuplesresultwidget.ui + querytool/QueryTab.ui + ProcessStdioWidget.ui + crud/CrudTab.ui + CodeGenerator.ui + widgets/SingleRecordWidget.ui + ) add_executable(pglab - 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 + ${PGLAB_SOURCES} + ${PGLAB_FORMS} resources.qrc ) -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") +if(WIN32) + set_target_properties(pglab PROPERTIES WIN32_EXECUTABLE ON) endif() -target_link_libraries( pglab - core +target_link_libraries(pglab PRIVATE + pglablib pgsql - boost-system - Qt5::Widgets - pthread + 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 ) diff --git a/pglab/CreateDatabaseDialog.h b/pglab/CreateDatabaseDialog.h index bcc00af..74cb209 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 f17dbac..a16e077 100644 --- a/pglab/CreateDatabaseDialog.ui +++ b/pglab/CreateDatabaseDialog.ui @@ -148,7 +148,7 @@ CodeEditor QPlainTextEdit -
codeeditor\CodeEditor.h
+
codeeditor/CodeEditor.h
diff --git a/pglab/catalog/models/DatabasesTableModel.cpp b/pglab/catalog/models/DatabasesTableModel.cpp index ac10ec5..289eea2 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 = d.size.totalBytes; + v = QVariant::fromValue(d.size.totalBytes); break; case AclCol: v = db.aclString(); diff --git a/pglab/catalog/models/IndexModel.cpp b/pglab/catalog/models/IndexModel.cpp index 7cee4e4..e04f715 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 = dat.sizeBytes; + v = QVariant::fromValue(dat.sizeBytes); break; case ExplicitIndexCol: v = !dat.isSupportingIndex(); diff --git a/pglab/catalog/models/SequenceModel.cpp b/pglab/catalog/models/SequenceModel.cpp index 659348f..640b50c 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 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 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 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 0df8ff2..d6a4e8b 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() : 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 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); } return QVariant(); diff --git a/pglab/crud/CrudModel.cpp b/pglab/crud/CrudModel.cpp index d688bd6..8367f32 100644 --- a/pglab/crud/CrudModel.cpp +++ b/pglab/crud/CrudModel.cpp @@ -150,7 +150,7 @@ QVariant StringToVariant(const std::string &str, Oid oid) case Pgsql::oid_oid: case Pgsql::int8_oid: - return v.operator int64_t(); + return QVariant::fromValue(static_cast(v)); case Pgsql::bool_oid: return v.operator bool(); diff --git a/pglab/querytool/QueryExplainModel.cpp b/pglab/querytool/QueryExplainModel.cpp index 51381ec..bcce73b 100644 --- a/pglab/querytool/QueryExplainModel.cpp +++ b/pglab/querytool/QueryExplainModel.cpp @@ -40,7 +40,7 @@ if (role == Qt::DisplayRole) { result = item->estimateError(); break; case c_ColumnRowCount: - result = item->actualRows; + result = QVariant::fromValue(item->actualRows); break; case c_ColumnLoops: result = item->actualLoops; diff --git a/pglablib/ASyncDBConnection.cpp b/pglablib/ASyncDBConnection.cpp index 1c27fb9..2b5c2fb 100644 --- a/pglablib/ASyncDBConnection.cpp +++ b/pglablib/ASyncDBConnection.cpp @@ -1,13 +1,14 @@ -#include "ASyncDBConnection.h" +#include "ASyncDBConnection.h" #include "ScopeGuard.h" #include "util.h" #include "Pgsql_PgException.h" -#include "Win32Event.h" -#include "WaitHandleList.h" +#include #include #include -#include #include +#include +#include +#include namespace { @@ -45,10 +46,6 @@ public: struct t_Command { std::mutex m_mutex; t_CommandQueue m_queue; - Win32Event m_newEvent; - t_Command() - : m_newEvent(Win32Event::Reset::Auto, Win32Event::Initial::Clear) - {} } m_commandQueue; ConnectionConfig m_config; @@ -64,18 +61,29 @@ public: void stop(); + /// Wakes the worker thread when it is waiting in its event loop + void wake(); + private: + enum class WaitResult { + Socket, ///< the socket became ready + Wakeup, ///< woken up by stop() or by a new command + Timeout ///< the timeout expired + }; + ASyncDBConnection *asyncConnObject = nullptr; - /// \todo Implement new method to stop the thread - Win32Event m_stopEvent; + std::atomic terminateRequested{false}; ///< is set when the thread should stop + std::mutex m_wakeMutex; + QEventLoop *m_loop = nullptr; ///< loop the worker blocks in, guarded by m_wakeMutex Pgsql::Connection m_connection; - bool terminateRequested = false; ///< is set when the thread should stop bool m_terminated = true; Pgsql::Canceller m_canceller; QElapsedTimer m_timer; + bool hasQueuedCommand(); + WaitResult waitForSocket(QSocketNotifier::Type type, int timeout_ms, bool wakeOnNewCommand); bool makeConnection(); void communicate(); @@ -97,7 +105,6 @@ private: ASyncDBConnectionThread::ASyncDBConnectionThread(ASyncDBConnection *asco) : asyncConnObject(asco) - , m_stopEvent(Win32Event::Reset::Manual, Win32Event::Initial::Clear) {} void ASyncDBConnectionThread::run() @@ -154,6 +161,59 @@ bool ASyncDBConnectionThread::cancel() return m_canceller.cancel(nullptr); } +bool ASyncDBConnectionThread::hasQueuedCommand() +{ + std::lock_guard lg(m_commandQueue.m_mutex); + return !m_commandQueue.m_queue.empty(); +} + +ASyncDBConnectionThread::WaitResult ASyncDBConnectionThread::waitForSocket(QSocketNotifier::Type type, int timeout_ms, bool wakeOnNewCommand) +{ + QEventLoop loop; + WaitResult result = WaitResult::Wakeup; + + QSocketNotifier notifier(m_connection.socket(), type); + QObject::connect(¬ifier, &QSocketNotifier::activated, &loop, [&result, &loop] { + result = WaitResult::Socket; + loop.quit(); + }); + + QTimer timer; + timer.setSingleShot(true); + if (timeout_ms >= 0) { + QObject::connect(&timer, &QTimer::timeout, &loop, [&result, &loop] { + result = WaitResult::Timeout; + loop.quit(); + }); + timer.start(timeout_ms); + } + + { + std::lock_guard lg(m_wakeMutex); + m_loop = &loop; + } + + // Checked after publishing the loop, so a wake-up that happened before is not lost + bool pending = terminateRequested || (wakeOnNewCommand && hasQueuedCommand()); + if (!pending) { + loop.exec(); + } + + { + std::lock_guard lg(m_wakeMutex); + m_loop = nullptr; + } + return result; +} + +void ASyncDBConnectionThread::wake() +{ + std::lock_guard lg(m_wakeMutex); + if (m_loop) { + QMetaObject::invokeMethod(m_loop, [loop = m_loop] { loop->quit(); }, Qt::QueuedConnection); + } +} + bool ASyncDBConnectionThread::makeConnection() { using namespace std::literals::chrono_literals; @@ -165,30 +225,14 @@ bool ASyncDBConnectionThread::makeConnection() bool ok = m_connection.connectStart(conn_string); //keywords, values); auto start = std::chrono::steady_clock::now(); if (ok && m_connection.status() != CONNECTION_BAD) { - Win32Event socket_event(Win32Event::Reset::Auto, Win32Event::Initial::Clear); - - long fd = FD_WRITE; + QSocketNotifier::Type type = QSocketNotifier::Write; while (true) { // poll till complete or failed (we can get an abort command) - int sock = m_connection.socket(); - WSAEventSelect(sock, socket_event.handle(), fd); - WaitHandleList whl; - auto wait_result_socket_event = whl.add(socket_event); - auto wait_result_stop = whl.add(m_stopEvent); + auto elapsed = std::chrono::steady_clock::now() - start; + auto remaining = std::chrono::duration_cast(30s - elapsed).count(); + WaitResult res = waitForSocket(type, remaining > 0 ? static_cast(remaining) : 0, false); - auto nu = std::chrono::steady_clock::now(); - std::chrono::duration diff = -(nu-start); - diff += 30s; - DWORD timeout = diff.count(); - - DWORD res = MsgWaitForMultipleObjectsEx( - whl.count(), // _In_ DWORD nCount, - whl, // _In_ const HANDLE *pHandles, - timeout, // _In_ DWORD dwMilliseconds, - 0, // _In_ DWORD dwWakeMask, - 0 // _In_ DWORD dwFlags - ); - if (res == wait_result_socket_event) { + if (res == WaitResult::Socket) { auto poll_state = m_connection.connectPoll(); if (poll_state == PGRES_POLLING_OK) { qDebug() << "ASyncDBConnection Connected"; @@ -202,17 +246,21 @@ bool ASyncDBConnectionThread::makeConnection() } else if (poll_state == PGRES_POLLING_READING) { doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" }); - fd = FD_READ; + type = QSocketNotifier::Read; } else if (poll_state == PGRES_POLLING_WRITING) { doStateCallback({ ASyncDBConnection::State::Connecting, "Negotiating" }); - fd = FD_WRITE; + type = QSocketNotifier::Write; } } - else if (res == wait_result_stop) { + else if (terminateRequested) { + m_connection.close(); + return false; + } + else if (res == WaitResult::Timeout) { + doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection timed out" }); m_connection.close(); return false; - } } // end while (true) } @@ -252,7 +300,7 @@ void ASyncDBConnectionThread::communicate() void ASyncDBConnectionThread::stop() { terminateRequested = true; - m_stopEvent.set(); + wake(); } void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state) @@ -264,34 +312,18 @@ void ASyncDBConnectionThread::doStateCallback(ASyncDBConnection::StateData state bool ASyncDBConnectionThread::waitForAndSendCommand() { - SOCKET sock = static_cast(m_connection.socket()); - Win32Event socket_event(Win32Event::Reset::Manual, Win32Event::Initial::Clear); - long fd = FD_READ | FD_CLOSE; - WSAEventSelect(sock, socket_event.handle(), fd); - - WaitHandleList whl; - auto wait_result_new_command = whl.add(m_commandQueue.m_newEvent); - auto wait_result_socket = whl.add(socket_event); - whl.add(m_stopEvent); - - DWORD res = MsgWaitForMultipleObjectsEx( - whl.count(), // _In_ DWORD nCount, - whl, // _In_ const HANDLE *pHandles, - INFINITE, // _In_ DWORD dwMilliseconds, - 0, // _In_ DWORD dwWakeMask, - 0 // _In_ DWORD dwFlags - ); - if (res == wait_result_new_command) { + if (hasQueuedCommand()) { doNewCommand(); + return true; + } + + // Idle connection: a readable socket carries notifies or notices, or signals a lost connection + if (waitForSocket(QSocketNotifier::Read, -1, true) == WaitResult::Socket) { + if (!m_connection.consumeInput()) { + doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection lost" }); + return false; + } } - else if (res == wait_result_socket) { - WSANETWORKEVENTS net_events; - WSAEnumNetworkEvents(sock, socket_event.handle(), &net_events); - if (net_events.lNetworkEvents & FD_CLOSE) { - doStateCallback({ ASyncDBConnection::State::NotConnected, "Connection lost" }); - return false; - } - } // Note if it was stop we can just return and function // above will stop looping because terminateRequested has been set too by stop return true; @@ -347,42 +379,16 @@ bool ASyncDBConnectionThread::consumeResultInput() void ASyncDBConnectionThread::waitForResult() { - SOCKET sock = static_cast(m_connection.socket()); - Win32Event socket_event(Win32Event::Reset::Manual, Win32Event::Initial::Clear); - - long fd = FD_READ | FD_CLOSE; - bool finished = false; while ( ! finished) { - WSAEventSelect(sock, socket_event.handle(), fd); + WaitResult res = waitForSocket(QSocketNotifier::Read, -1, false); - WaitHandleList whl; - auto wait_result_socket = whl.add(socket_event); - auto wait_result_stop = whl.add(m_stopEvent); - - DWORD res = MsgWaitForMultipleObjectsEx( - whl.count(), // _In_ DWORD nCount, - whl, // _In_ const HANDLE *pHandles, - INFINITE, // _In_ DWORD dwMilliseconds, - 0, // _In_ DWORD dwWakeMask, - 0 // _In_ DWORD dwFlags - ); - if (res == wait_result_socket) { - WSANETWORKEVENTS net_events; - WSAEnumNetworkEvents(sock, socket_event.handle(), &net_events); - - if (net_events.lNetworkEvents & FD_READ) { - if (consumeResultInput()) { - finished = true; - } + if (res == WaitResult::Socket) { + if (consumeResultInput()) { + finished = true; } - else if (net_events.lNetworkEvents & FD_CLOSE) { - doStateCallback({ ASyncDBConnection::State::NotConnected, "Close while waiting for result" }); - finished = true; - stop(); - } } - if (res == wait_result_stop) { + else if (terminateRequested) { // Send cancel, close connection and terminate thread cancel(); doStateCallback(ASyncDBConnection::State::Terminating); @@ -455,8 +461,8 @@ bool ASyncDBConnection::send(const std::string &command, on_result_callback on_r { std::lock_guard lg(m_threadData->m_commandQueue.m_mutex); m_threadData->m_commandQueue.m_queue.emplace(command, on_result); - m_threadData->m_commandQueue.m_newEvent.set(); } + m_threadData->wake(); return true; } @@ -468,8 +474,8 @@ bool ASyncDBConnection::send(const std::string &command, Pgsql::Params params, o { std::lock_guard lg(m_threadData->m_commandQueue.m_mutex); m_threadData->m_commandQueue.m_queue.emplace(command, std::move(params), on_result); - m_threadData->m_commandQueue.m_newEvent.set(); } + m_threadData->wake(); return true; } diff --git a/pglablib/ASyncDBConnection.h b/pglablib/ASyncDBConnection.h index 02f5310..0479f5f 100644 --- a/pglablib/ASyncDBConnection.h +++ b/pglablib/ASyncDBConnection.h @@ -11,6 +11,7 @@ #include #include #include +#include class ASyncDBConnectionThread; /** \brief Class that handles asynchronous execution of queries. @@ -33,7 +34,7 @@ public: class StateData { public: - State State; + enum State State; QString Message; StateData(enum State state, QString message = QString()) diff --git a/pglablib/CMakeLists.txt b/pglablib/CMakeLists.txt new file mode 100644 index 0000000..a3c759d --- /dev/null +++ b/pglablib/CMakeLists.txt @@ -0,0 +1,187 @@ +add_library(pglablib STATIC + Pglablib.cpp + ASyncDBConnection.cpp + ConnectionConfig.cpp + TestConnection.cpp + catalog/PgType.cpp + catalog/PgTypeContainer.cpp + catalog/PgNamespace.cpp + catalog/PgClass.cpp + catalog/PgDatabase.cpp + catalog/PgDatabaseContainer.cpp + catalog/PgAuthId.cpp + catalog/PgAuthIdContainer.cpp + catalog/PgClassContainer.cpp + catalog/PgDatabaseCatalog.cpp + catalog/PgNamespaceContainer.cpp + catalog/PgAttribute.cpp + catalog/PgContainer.cpp + catalog/PgAttributeContainer.cpp + catalog/PgIndex.cpp + catalog/PgIndexContainer.cpp + catalog/PgConstraint.cpp + catalog/PgConstraintContainer.cpp + ParamListJson.cpp + ParamListModel.cpp + sqlite/SQLiteConnection.cpp + sqlite/sqlite3.c + ui/catalog/tables/TableNode.cpp + ui/catalog/tables/TableSize.cpp + ui/catalog/tables/TableTreeBuilder.cpp + util.cpp + SqlFormattingUtils.cpp + catalog/PgKeywordList.cpp + QueryGenerator.cpp + catalog/PgObject.cpp + catalog/PgTablespace.cpp + catalog/PgTablespaceContainer.cpp + codebuilder/LanguageConfig.cpp + codebuilder/CodeBuilder.cpp + codebuilder/NameManglingRules.cpp + codebuilder/DefaultConfigs.cpp + codebuilder/TypeMappings.cpp + codebuilder/IndentationConfig.cpp + codebuilder/StructureTemplate.cpp + FormatToStream.cpp + codebuilder/StringLiteralRules.cpp + codebuilder/StringEscapeRule.cpp + catalog/PgTrigger.cpp + catalog/PgTriggerContainer.cpp + catalog/PgProc.cpp + catalog/PgProcContainer.cpp + catalog/PgDatabaseObject.cpp + catalog/PgServerObject.cpp + catalog/PgNamespaceObject.cpp + catalog/PgCollation.cpp + catalog/PgCollationContainer.cpp + catalog/PgInherits.cpp + catalog/PgInheritsContainer.cpp + SelectionEditorFactory.cpp + catalog/PgAm.cpp + model/TypeModelFactory.cpp + model/TypeSelectionItemModel.cpp + catalog/PgAmContainer.cpp + model/CollationModel.cpp + model/CollationModelFactory.cpp + catalog/PgLanguageContainer.cpp + catalog/PgLanguage.cpp + catalog/PgAcl.cpp + catalog/PgSequence.cpp + catalog/PgSequenceContainer.cpp + utils/HumanReadableBytes.cpp + utils/KeyStrengthener.cpp + utils/PasswordManager.cpp + utils/PostgresqlUrlParser.cpp + Pglablib.h + ASyncDBConnection.h + ConnectionConfig.h + TestConnection.h + catalog/PgType.h + catalog/PgTypeContainer.h + catalog/PgNamespace.h + catalog/PgClass.h + catalog/PgDatabase.h + catalog/PgDatabaseContainer.h + catalog/PgContainer.h + catalog/PgAuthId.h + catalog/PgAuthIdContainer.h + catalog/PgClassContainer.h + catalog/PgDatabaseCatalog.h + catalog/PgNamespaceContainer.h + catalog/PgAttribute.h + catalog/PgAttributeContainer.h + catalog/PgIndex.h + catalog/PgIndexContainer.h + catalog/PgConstraint.h + catalog/PgConstraintContainer.h + ParamListJson.h + ParamListModel.h + sqlite/SQLiteConnection.h + sqlite/sqlite3.h + sqlite/sqlite3ext.h + ui/catalog/tables/TableNode.h + ui/catalog/tables/TableSize.h + ui/catalog/tables/TableTreeBuilder.h + util.h + SqlFormattingUtils.h + catalog/PgCatalogTypes.h + catalog/PgKeywordList.h + QueryGenerator.h + catalog/PgObject.h + catalog/PgTablespace.h + catalog/PgTablespaceContainer.h + codebuilder/LanguageConfig.h + codebuilder/CodeBuilder.h + codebuilder/NameManglingRules.h + codebuilder/DefaultConfigs.h + codebuilder/TypeMappings.h + codebuilder/IndentationConfig.h + codebuilder/StructureTemplate.h + FormatToStream.h + codebuilder/ResultLoopTemplate.h + codebuilder/StringEscapeRule.h + codebuilder/StringLiteralRules.h + catalog/PgTrigger.h + catalog/PgTriggerContainer.h + catalog/PgProc.h + catalog/PgProcContainer.h + catalog/PgDatabaseObject.h + catalog/PgServerObject.h + catalog/PgNamespaceObject.h + catalog/PgCollation.h + catalog/PgCollationContainer.h + AbstractModelFactory.h + AbstractEditorFactory.h + catalog/PgInherits.h + catalog/PgInheritsContainer.h + SelectionEditorFactory.h + model/TypeSelectionItemModel.h + model/TypeModelFactory.h + catalog/PgAm.h + catalog/PgAmContainer.h + model/CollationModel.h + model/CollationModelFactory.h + catalog/PgLanguageContainer.h + catalog/PgLanguage.h + catalog/PgAcl.h + catalog/PgSequence.h + catalog/PgSequenceContainer.h + utils/HumanReadableBytes.h + utils/KeyStrengthener.h + utils/PasswordManager.h + utils/PostgresqlUrlParser.h + ) + +find_package(Boost REQUIRED COMPONENTS container timer chrono assert) + +find_path(BOTAN_INCLUDE_DIR botan/hash.h REQUIRED) +find_library(BOTAN_LIBRARY NAMES botan-3 botan REQUIRED) +if(NOT TARGET Botan::Botan) + add_library(Botan::Botan STATIC IMPORTED) + set_target_properties(Botan::Botan PROPERTIES + IMPORTED_LOCATION "${BOTAN_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${BOTAN_INCLUDE_DIR}" + ) +endif() + +target_link_libraries(pglablib PUBLIC + core + pgsql + Boost::container + Boost::timer + Boost::chrono + Boost::assert + Botan::Botan + Qt6::Core + Qt6::Widgets + ) + +target_include_directories(pglablib PUBLIC + $ + ) + +set_target_properties(pglablib PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ON + ) diff --git a/pglablib/SqlFormattingUtils.cpp b/pglablib/SqlFormattingUtils.cpp index a75620e..ff7f1d3 100644 --- a/pglablib/SqlFormattingUtils.cpp +++ b/pglablib/SqlFormattingUtils.cpp @@ -200,7 +200,7 @@ QString quoteIdent(QString ident) { assert(ident.length() > 0); - static const wchar_t dquote = L'"'; + static const QChar dquote = u'"'; if (identNeedsQuotes(ident)) { QString out; diff --git a/pglablib/WaitHandleList.cpp b/pglablib/WaitHandleList.cpp deleted file mode 100644 index d219610..0000000 --- a/pglablib/WaitHandleList.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "WaitHandleList.h" -#include "win32event.h" - -WaitHandleList::WaitHandleList() = default; -WaitHandleList::~WaitHandleList() = default; - -WaitResultValue WaitHandleList::add(HANDLE h) -{ - m_waitHandles.push_back(h); - return WAIT_OBJECT_0 + static_cast(m_waitHandles.size() - 1); -} - -WaitResultValue WaitHandleList::add(Win32Event &e) -{ - return add(e.handle()); -} - -DWORD WaitHandleList::count() const -{ - return static_cast(m_waitHandles.size()); -} - -void WaitHandleList::clear() -{ - m_waitHandles.clear(); -} - -WaitHandleList::operator const HANDLE*() const -{ - return m_waitHandles.data(); -} diff --git a/pglablib/WaitHandleList.h b/pglablib/WaitHandleList.h deleted file mode 100644 index 376e817..0000000 --- a/pglablib/WaitHandleList.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef WAITHANDLELIST_H -#define WAITHANDLELIST_H - -#include -#include - -class Win32Event; - -using WaitResultValue = DWORD; - -class WaitHandleList { -public: - WaitHandleList(); - ~WaitHandleList(); - - WaitResultValue add(HANDLE h); - WaitResultValue add(Win32Event &e); - DWORD count() const; - void clear(); - operator const HANDLE*() const; - -private: - std::vector m_waitHandles; -}; - -#endif // WAITHANDLELIST_H diff --git a/pglablib/Win32Event.h b/pglablib/Win32Event.h deleted file mode 100644 index 684d13f..0000000 --- a/pglablib/Win32Event.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef WIN32EVENT_H -#define WIN32EVENT_H - -#include - -#include -/** Simpel wrapper around a Win32 Event object. - -Mostly to make cleanup automatic.*/ -class Win32Event { -public: - enum class Reset { Auto=0, Manual=1 }; - enum class Initial { Clear=0, Set=1 }; - - Win32Event(Reset r, Initial is) - : hEvent(CreateEvent( - nullptr, // _In_opt_ LPSECURITY_ATTRIBUTES lpEventAttributes, - BOOL(r), // _In_ BOOL bManualReset, - BOOL(is), // _In_ BOOL bInitialState, - nullptr //_In_opt_ LPCTSTR lpName - )) - {} - - Win32Event(Reset r, Initial is, int sock, long net_events) - : Win32Event(r, is) - { - WSAEventSelect(sock, hEvent, net_events); - } - - ~Win32Event() - { - CloseHandle(hEvent); - } - - Win32Event(const Win32Event &) = delete; - Win32Event &operator=(const Win32Event &) = delete; - - void set() { SetEvent(hEvent); } - - void reset() { ResetEvent(hEvent); } - - HANDLE handle() { return hEvent; } -private: - HANDLE hEvent; -}; - -#endif // WIN32EVENT_H diff --git a/pglablib/catalog/PgLanguage.cpp b/pglablib/catalog/PgLanguage.cpp index cd9dd4f..c224d23 100644 --- a/pglablib/catalog/PgLanguage.cpp +++ b/pglablib/catalog/PgLanguage.cpp @@ -1,13 +1,15 @@ #include "PgLanguage.h" +#include + QString PgLanguage::createSql() const { - throw std::exception("PgLanguage::createSql() not implemented"); + throw std::logic_error("PgLanguage::createSql() not implemented"); } QString PgLanguage::dropSql() const { - throw std::exception("PgLanguage::dropSql() not implemented"); + throw std::logic_error("PgLanguage::dropSql() not implemented"); } QString PgLanguage::typeName() const diff --git a/pglablib/catalog/PgProc.cpp b/pglablib/catalog/PgProc.cpp index 83fe734..02cf6d0 100644 --- a/pglablib/catalog/PgProc.cpp +++ b/pglablib/catalog/PgProc.cpp @@ -48,15 +48,15 @@ namespace { for(; index < len; index++) { QChar ch = str[index]; - if (!double_quote && ch == L'\'') + if (!double_quote && ch == u'\'') single_quote = !single_quote; - else if (!single_quote && ch == L'"') + else if (!single_quote && ch == u'"') double_quote = !double_quote; - else if (!double_quote && !single_quote && ch == L'(') + else if (!double_quote && !single_quote && ch == u'(') brackets++; - else if (!double_quote && !single_quote && ch == L')') + else if (!double_quote && !single_quote && ch == u')') brackets--; - else if (!double_quote && !single_quote && brackets == 0 && ch == L',') { + else if (!double_quote && !single_quote && brackets == 0 && ch == u',') { if (index != start_array) res.push_back(str.mid(start_array, index - 1).trimmed()); else diff --git a/pglablib/catalog/PgServerObject.cpp b/pglablib/catalog/PgServerObject.cpp index f85bd1f..1108917 100644 --- a/pglablib/catalog/PgServerObject.cpp +++ b/pglablib/catalog/PgServerObject.cpp @@ -38,7 +38,7 @@ QString PgServerObject::alterOwnerSql(const QString& ident) const } -void PgServerObject::setAcls(boost::optional acls) +void PgServerObject::setAcls(std::optional acls) { m_acls = std::move(acls); } diff --git a/pglablib/catalog/PgServerObject.h b/pglablib/catalog/PgServerObject.h index 5c08fd3..b1520a1 100644 --- a/pglablib/catalog/PgServerObject.h +++ b/pglablib/catalog/PgServerObject.h @@ -4,7 +4,7 @@ #include #include "PgObject.h" #include "PgAcl.h" -#include +#include class PgAuthId; @@ -26,7 +26,7 @@ public: * @brief setAcls * @param acls Important: pass empty optional when acl IS NULL, pass empty list for empty array */ - void setAcls(boost::optional acls); + void setAcls(std::optional acls); QString aclString() const; /** * @brief grantSql @@ -53,7 +53,7 @@ protected: private: Oid m_ownerOid = InvalidOid; const PgAuthId * m_owner = nullptr; - boost::optional m_acls; + std::optional m_acls; }; #endif // PGSERVEROBJECT_H diff --git a/pglablib/codebuilder/StringEscapeRule.h b/pglablib/codebuilder/StringEscapeRule.h index a8c9822..fb01dd8 100644 --- a/pglablib/codebuilder/StringEscapeRule.h +++ b/pglablib/codebuilder/StringEscapeRule.h @@ -2,7 +2,6 @@ #define STRINGESCAPERULE_H #include -#include enum class ConvertToNumericEscape { Utf8, ///< Generates upto four escapes for 4 utf8 chars diff --git a/pglablib/utils/HumanReadableBytes.h b/pglablib/utils/HumanReadableBytes.h index b76de05..b17ec0f 100644 --- a/pglablib/utils/HumanReadableBytes.h +++ b/pglablib/utils/HumanReadableBytes.h @@ -1,5 +1,6 @@ #pragma once +#include #include std::string HumanReadableBytes(uint64_t bytes); diff --git a/pglablib/utils/PasswordManager.cpp b/pglablib/utils/PasswordManager.cpp index b0a71de..dfa0476 100644 --- a/pglablib/utils/PasswordManager.cpp +++ b/pglablib/utils/PasswordManager.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include using namespace Botan; @@ -33,7 +33,7 @@ public: * distinct key which is derived by hashing the wrapped key name with HMAC. */ const std::vector wrapped_name = - nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), + nist_key_wrap_padded(reinterpret_cast(name.data()), name.size(), *m_cipher); @@ -47,7 +47,7 @@ public: secure_vector get(const std::string& name, const std::string_view &wrapped_key) const { const std::vector wrapped_name = - nist_key_wrap_padded(cast_char_ptr_to_uint8(name.data()), + nist_key_wrap_padded(reinterpret_cast(name.data()), name.size(), *m_cipher); diff --git a/pglablib/utils/PasswordManager.h b/pglablib/utils/PasswordManager.h index 4bba574..8fb99d1 100644 --- a/pglablib/utils/PasswordManager.h +++ b/pglablib/utils/PasswordManager.h @@ -19,7 +19,11 @@ namespace Botan { class PasswordManagerException: public std::exception { public: - using std::exception::exception; //(char const* const _Message); + PasswordManagerException() = default; + explicit PasswordManagerException(const char *message) : m_message(message) {} + const char *what() const noexcept override { return m_message.c_str(); } +private: + std::string m_message; }; class PasswordManagerLockedException: public PasswordManagerException { diff --git a/pgsql/ArrayParser.cpp b/pgsql/ArrayParser.cpp index 1f29a61..cdb5999 100644 --- a/pgsql/ArrayParser.cpp +++ b/pgsql/ArrayParser.cpp @@ -1,4 +1,5 @@ #include "ArrayParser.h" +#include #include using namespace Pgsql; diff --git a/pgsql/CMakeLists.txt b/pgsql/CMakeLists.txt new file mode 100644 index 0000000..f6b0f39 --- /dev/null +++ b/pgsql/CMakeLists.txt @@ -0,0 +1,48 @@ +find_package(PostgreSQL REQUIRED) + +add_library(pgsql STATIC + Pgsql_Connection.cpp + Pgsql_Params.cpp + Pgsql_PgException.cpp + Pgsql_Result.cpp + Pgsql_Row.cpp + Pgsql_Value.cpp + ArrayParser.cpp + Pgsql_oids.cpp + Pgsql_Transaction.cpp + Pgsql_ErrorDetails.cpp + Pgsql_Canceller.cpp + Pgsql_Connection.h + Pgsql_IResult.h + Pgsql_Params.h + Pgsql_PgException.h + Pgsql_Result.h + Pgsql_ResultConstIterator.h + Pgsql_Row.h + Pgsql_Value.h + Pgsql_declare.h + Pgsql_Col.h + ArrayParser.h + Pgsql_oids.h + SqlGenerator.h + Pgsql_Transaction.h + Pgsql_ErrorDetails.h + Pgsql_Canceller.h + ) + +target_link_libraries(pgsql PUBLIC + core + PostgreSQL::PostgreSQL + Qt6::Core + Qt6::Widgets + ) + +target_include_directories(pgsql PUBLIC + $ + ) + +set_target_properties(pgsql PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ON + ) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a02ee3c --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +find_package(GTest CONFIG REQUIRED) + +add_subdirectory(mygtestutils) +add_subdirectory(PgsqlTests) +add_subdirectory(pglabtests) diff --git a/tests/PgsqlTests/CMakeLists.txt b/tests/PgsqlTests/CMakeLists.txt new file mode 100644 index 0000000..34071a1 --- /dev/null +++ b/tests/PgsqlTests/CMakeLists.txt @@ -0,0 +1,24 @@ +add_executable(PgsqlTests + main.cpp + tst_Value.cpp + tst_ArrayParser.cpp + tst_Pgsql_oids.cpp + ) + +target_link_libraries(PgsqlTests PRIVATE + pgsql + mygtestutils + GTest::gtest + Qt6::Core + ) + +target_include_directories(PgsqlTests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +set_target_properties(PgsqlTests PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + ) + +add_test(NAME PgsqlTests COMMAND PgsqlTests) diff --git a/tests/mygtestutils/CMakeLists.txt b/tests/mygtestutils/CMakeLists.txt new file mode 100644 index 0000000..930a0ad --- /dev/null +++ b/tests/mygtestutils/CMakeLists.txt @@ -0,0 +1,19 @@ +add_library(mygtestutils STATIC + PrintTo_Qt.cpp + PrintTo_Qt.h + ) + +target_link_libraries(mygtestutils PUBLIC + GTest::gtest + Qt6::Core + ) + +target_include_directories(mygtestutils PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +set_target_properties(mygtestutils PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + POSITION_INDEPENDENT_CODE ON + ) diff --git a/tests/pglabtests/CMakeLists.txt b/tests/pglabtests/CMakeLists.txt new file mode 100644 index 0000000..f13dc29 --- /dev/null +++ b/tests/pglabtests/CMakeLists.txt @@ -0,0 +1,41 @@ +add_executable(pglabtests + main.cpp + TableTreeBuilderTests.cpp + tst_ConvertLangToSqlString.cpp + tst_ConvertToMultiLineCString.cpp + tst_ExplainJsonParser.cpp + tst_HumanReadableBytes.cpp + tst_TryParsePostgresqlUrl.cpp + tst_escapeConnectionStringValue.cpp + tst_expected.cpp + tst_SqlLexer.cpp + tst_scopeguard.cpp + tst_CsvWriter.cpp + tst_PasswordManager.cpp + tst_ParamJson.cpp + tst_CodeBuilder.cpp + tst_NameManglingRules.cpp + tst_TypeMappings.cpp + tst_Range.cpp + ) + +target_link_libraries(pglabtests PRIVATE + pglablib + pgsql + core + mygtestutils + GTest::gtest + Qt6::Core + Qt6::Widgets + ) + +target_include_directories(pglabtests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +set_target_properties(pglabtests PROPERTIES + CXX_STANDARD 20 + CXX_STANDARD_REQUIRED ON + ) + +add_test(NAME pglabtests COMMAND pglabtests) diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000..4283df6 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,13 @@ +{ + "name": "pglab", + "version-string": "0.1.0", + "dependencies": [ + "boost-container", + "boost-timer", + "boost-chrono", + "boost-assert", + "botan", + "libpq", + "gtest" + ] +}