diff --git a/.clang-format b/.clang-format index 5d94852..dd1841f 100644 --- a/.clang-format +++ b/.clang-format @@ -86,6 +86,8 @@ Standard: Cpp11 StatementMacros: - Q_UNUSED - QT_REQUIRE_VERSION +WhitespaceSensitiveMacros: + - C_ATTR UseTab: Never ... diff --git a/CMakeLists.txt b/CMakeLists.txt index 3d07814..f9ae283 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ # SPDX-License-Identifier: MIT cmake_minimum_required(VERSION 3.16) -project(libasql VERSION 0.79.0 LANGUAGES CXX) +project(libasql VERSION 0.80.0 LANGUAGES CXX) include(GNUInstallDirs) diff --git a/demos/async1/async1.cpp b/demos/async1/async1.cpp index 7043914..836b9d0 100644 --- a/demos/async1/async1.cpp +++ b/demos/async1/async1.cpp @@ -23,10 +23,47 @@ using namespace ASql; +void recursiveLoop() +{ + auto db = APool::database(u"memory_loop"); + db.exec(u"SELECT now()", {QJsonObject{{QStringLiteral("foo"), true}}}, nullptr, [](AResult &result) { + if (result.error()) { + qDebug() << "Error memory_loop" << result.errorString(); + } else { + recursiveLoop(); + } + }); +} + int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); + { + APool::create(APg::factory(QStringLiteral("postgres:///")), u"move_db_pool"); + APool::setMaxConnections(1, u"move_db_pool"); + APool::database( + nullptr, [](ADatabase db) { + db.exec(u"SELECT 'I ♥ Cutelyst!' AS utf8", nullptr, [](AResult &result) { + qDebug() << "=====iterator single row" << result.toHash(); + if (result.error()) { + qDebug() << "Error" << result.errorString(); + } + }); + }, + u"move_db_pool"); + APool::database( + nullptr, [](ADatabase db) { + db.exec(u"SELECT 'I ♥ Cutelyst!' AS utf8", nullptr, [](AResult &result) { + qDebug() << "=====iterator single row" << result.toHash(); + if (result.error()) { + qDebug() << "Error" << result.errorString(); + } + }); + }, + u"move_db_pool"); + } + { // regresion test crash - where selfDriver gets released APool::create(APg::factory(QStringLiteral("postgres:///")), u"delete_db_after_use"); @@ -42,6 +79,19 @@ int main(int argc, char *argv[]) } } + { + // memory loop + APool::create(APg::factory(QStringLiteral("postgres:///")), u"memory_loop"); + APool::setMaxIdleConnections(5, u"memory_loop"); + // APool::setMaxConnections(0, u"memory_loop"); + + { + for (int i = 0; i < 20; ++i) { + recursiveLoop(); + } + } + } + APool::create(APg::factory(QStringLiteral("postgres:///"))); APool::setMaxIdleConnections(10); diff --git a/demos/async1/deleter.cpp b/demos/async1/deleter.cpp index 07716e4..331c696 100644 --- a/demos/async1/deleter.cpp +++ b/demos/async1/deleter.cpp @@ -27,14 +27,14 @@ int main(int argc, char *argv[]) QCoreApplication app(argc, argv); APool::create(APg::factory(QStringLiteral("postgres:///?target_session_attrs=read-write"))); - APool::setSetupCallback([](ADatabase &db) { + APool::setSetupCallback([](ADatabase db) { qDebug() << "setup db"; db.exec(u"SET TIME ZONE 'Europe/Rome';", nullptr, [](AResult &result) { qDebug() << "SETUP" << result.error() << result.errorString() << result.toJsonObject(); }); }); - APool::setReuseCallback([](ADatabase &db) { + APool::setReuseCallback([](ADatabase db) { qDebug() << "reuse db"; db.exec(u"DISCARD ALL", nullptr, [](AResult &result) { qDebug() << "REUSE" << result.error() << result.errorString() << result.toJsonObject(); diff --git a/demos/async1/prepared.cpp b/demos/async1/prepared.cpp index 41d8c72..cf8997f 100644 --- a/demos/async1/prepared.cpp +++ b/demos/async1/prepared.cpp @@ -97,7 +97,7 @@ int main(int argc, char *argv[]) // ADatabase().rollback(); assert - APool::database(nullptr, [=](ADatabase &db) { + APool::database(nullptr, [=](ADatabase db) { qDebug() << "Got db" << db.isOpen() << db.state(); db.exec(QStringLiteral("SELECT now()"), nullptr, [=](AResult &result) { diff --git a/src/adatabase.cpp b/src/adatabase.cpp index 3d947b3..c4e11b9 100644 --- a/src/adatabase.cpp +++ b/src/adatabase.cpp @@ -19,6 +19,11 @@ ADatabase::ADatabase(const std::shared_ptr &driver) { } +ADatabase::ADatabase(std::shared_ptr &&driver) + : d(driver) +{ +} + ADatabase::ADatabase(const std::shared_ptr &factory) : d(factory->createDriver()) { diff --git a/src/adatabase.h b/src/adatabase.h index 74ce1bb..498c9da 100644 --- a/src/adatabase.h +++ b/src/adatabase.h @@ -52,6 +52,11 @@ class ASQL_EXPORT ADatabase */ ADatabase(const std::shared_ptr &driver); + /*! + * \brief ADatabase contructs an database object with the supplied driver + */ + ADatabase(std::shared_ptr &&driver); + /*! * \brief ADatabase contructs an database object with the supplied driver factory */ diff --git a/src/apool.cpp b/src/apool.cpp index 45e0220..c9e687a 100644 --- a/src/apool.cpp +++ b/src/apool.cpp @@ -18,7 +18,7 @@ Q_LOGGING_CATEGORY(ASQL_POOL, "asql.pool", QtInfoMsg) using namespace ASql; struct APoolQueuedClient { - std::function cb; + ADatabaseFn cb; QPointer receiver; bool checkReceiver; }; @@ -28,8 +28,8 @@ struct APoolInternal { std::shared_ptr driverFactory; QVector pool; std::queue connectionQueue; - std::function setupCb; - std::function reuseCb; + ADatabaseFn setupCb; + ADatabaseFn reuseCb; int maxIdleConnections = 1; int maximuConnections = 0; int connectionCount = 0; @@ -85,11 +85,10 @@ void APool::pushDatabaseBack(QStringView connectionName, ADriver *driver) continue; } - ADatabase db; - db.d = std::shared_ptr(driver, [connectionName](ADriver *driver) { + ADatabase db{std::shared_ptr(driver, [connectionName](ADriver *driver) { pushDatabaseBack(connectionName, driver); - }); - client.cb(db); + })}; + client.cb(std::move(db)); return; } @@ -114,7 +113,7 @@ ADatabase APool::database(QStringView poolName) APoolInternal &iPool = it.value(); if (iPool.pool.empty()) { if (iPool.maximuConnections && iPool.connectionCount >= iPool.maximuConnections) { - qWarning(ASQL_POOL) << "Maximum number of connections reached" << poolName << iPool.connectionCount << iPool.maximuConnections; + qCritical(ASQL_POOL) << "Maximum number of connections reached" << poolName << iPool.connectionCount << iPool.connectionQueue.size(); } else { ++iPool.connectionCount; auto driver = iPool.driverFactory->createRawDriver(); @@ -154,15 +153,14 @@ int APool::currentConnections(QStringView poolName) return 0; } -void APool::database(QObject *receiver, std::function cb, QStringView poolName) +void APool::database(QObject *receiver, ADatabaseFn cb, QStringView poolName) { - ADatabase db; auto it = m_connectionPool.find(poolName); if (it != m_connectionPool.end()) { APoolInternal &iPool = it.value(); if (iPool.pool.empty()) { if (iPool.maximuConnections && iPool.connectionCount >= iPool.maximuConnections) { - qInfo(ASQL_POOL) << "Maximum number of connections reached, queuing" << poolName << iPool.connectionCount << iPool.maximuConnections; + qInfo(ASQL_POOL) << "Maximum number of connections reached, queuing" << poolName << iPool.connectionCount << iPool.connectionQueue.size(); APoolQueuedClient queued; queued.cb = cb; queued.receiver = receiver; @@ -172,31 +170,42 @@ void APool::database(QObject *receiver, std::function cb, QSt } ++iPool.connectionCount; qDebug(ASQL_POOL) << "Creating a database connection for pool" << poolName; - db.d = std::shared_ptr(iPool.driverFactory->createRawDriver(), [poolName](ADriver *driver) { + ADatabase db{std::shared_ptr(iPool.driverFactory->createRawDriver(), [poolName](ADriver *driver) { pushDatabaseBack(poolName, driver); - }); + })}; if (iPool.setupCb) { iPool.setupCb(db); } + + db.open(); + if (cb) { + cb(std::move(db)); + } } else { qDebug(ASQL_POOL) << "Reusing a database connection from pool" << poolName; ADriver *priv = iPool.pool.takeLast(); - db.d = std::shared_ptr(priv, [poolName](ADriver *driver) { + ADatabase db{std::shared_ptr(priv, [poolName](ADriver *driver) { pushDatabaseBack(poolName, driver); - }); + })}; if (iPool.reuseCb) { iPool.reuseCb(db); } + + db.open(); + if (cb) { + cb(std::move(db)); + } } } else { qCritical(ASQL_POOL) << "Database pool NOT FOUND" << poolName; - } - db.open(); - if (cb) { - cb(db); + ADatabase db; + db.open(); + if (cb) { + cb(std::move(db)); + } } } @@ -220,7 +229,7 @@ void APool::setMaxConnections(int max, QStringView poolName) } } -void APool::setSetupCallback(std::function cb, QStringView poolName) +void APool::setSetupCallback(ADatabaseFn cb, QStringView poolName) { auto it = m_connectionPool.find(poolName); if (it != m_connectionPool.end()) { @@ -230,7 +239,7 @@ void APool::setSetupCallback(std::function cb, QStringView po } } -void APool::setReuseCallback(std::function cb, QStringView poolName) +void APool::setReuseCallback(ADatabaseFn cb, QStringView poolName) { auto it = m_connectionPool.find(poolName); if (it != m_connectionPool.end()) { diff --git a/src/apool.h b/src/apool.h index 6a64f34..224de0d 100644 --- a/src/apool.h +++ b/src/apool.h @@ -15,6 +15,8 @@ namespace ASql { +using ADatabaseFn = std::function; + class ASQL_EXPORT APool { public: @@ -80,7 +82,7 @@ class ASQL_EXPORT APool * \param receiver * \param connectionName */ - static void database(QObject *receiver, std::function cb, QStringView poolName = defaultPool); + static void database(QObject *receiver, ADatabaseFn cb, QStringView poolName = defaultPool); /*! * \brief setMaxIdleConnections maximum number of idle connections of the pool @@ -123,7 +125,7 @@ class ASQL_EXPORT APool * \param max * \param poolName */ - static void setSetupCallback(std::function cb, QStringView poolName = defaultPool); + static void setSetupCallback(ADatabaseFn cb, QStringView poolName = defaultPool); /*! * \brief setReuseCallback setup a connection before being reused @@ -141,7 +143,7 @@ class ASQL_EXPORT APool * \param max * \param poolName */ - static void setReuseCallback(std::function cb, QStringView poolName = defaultPool); + static void setReuseCallback(ADatabaseFn cb, QStringView poolName = defaultPool); private: inline static void pushDatabaseBack(QStringView connectionName, ADriver *driver);