From bc3ffbda10fe0140ee61fad7e02d1fb4850b8b0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Wed, 5 Feb 2025 15:47:51 +0100 Subject: [PATCH] added connection pool and tests --- CMakeLists.txt | 18 +- include/matador/sql/connection_pool.hpp | 188 ++++++++++++++++++ include/matador/sql/dialect.hpp | 2 +- include/matador/sql/dialect_builder.hpp | 1 + include/matador/utils/placeholder.hpp | 2 +- include/matador/utils/types.hpp | 1 + .../orm/query/query_update_intermediate.cpp | 2 +- source/orm/sql/dialect.cpp | 3 +- source/orm/sql/dialect_builder.cpp | 6 + .../orm/sql/interface/query_result_reader.cpp | 18 -- test/orm/CMakeLists.txt | 19 +- test/orm/backend/test_backend_service.cpp | 4 +- test/orm/backend/test_result_reader.cpp | 3 +- test/orm/backend/test_result_reader.hpp | 28 +++ test/orm/query/QueryBuilderTest.cpp | 18 +- test/orm/sql/ColumnTest.cpp | 2 +- test/orm/sql/ConnectionPoolTest.cpp | 176 ++++++++++++++++ test/orm/sql/FieldTest.cpp | 2 +- test/orm/utils/auto_reset_event.cpp | 27 +++ test/orm/utils/auto_reset_event.hpp | 28 +++ 20 files changed, 495 insertions(+), 53 deletions(-) create mode 100644 include/matador/sql/connection_pool.hpp create mode 100644 test/orm/sql/ConnectionPoolTest.cpp create mode 100644 test/orm/utils/auto_reset_event.cpp create mode 100644 test/orm/utils/auto_reset_event.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 90fad2c..dadcaf2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,13 +10,13 @@ include(cmake/CPM.cmake) include(CTest) set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +#set(CMAKE_CXX_STANDARD_REQUIRED ON) +#set(CMAKE_CXX_EXTENSIONS OFF) +#set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -SET(GCC_CLANG_COMMON_FLAGS "-Wall -Wextra -pedantic -ftemplate-backtrace-limit=0") -SET(GCC_CLANG_COMMON_FLAGS_DEBUG "-O0 -g -DDEBUG") -SET(GCC_CLANG_COMMON_FLAGS_RELEASE "-O1 -DNDEBUG") +#SET(GCC_CLANG_COMMON_FLAGS "-Wall -Wextra -pedantic -ftemplate-backtrace-limit=0") +#SET(GCC_CLANG_COMMON_FLAGS_DEBUG "-O0 -g -DDEBUG") +#SET(GCC_CLANG_COMMON_FLAGS_RELEASE "-O1 -DNDEBUG") SET(CMAKE_POSITION_INDEPENDENT_CODE ON) message(STATUS "C++ Compiler ID: ${CMAKE_CXX_COMPILER_ID}") @@ -30,9 +30,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS_RELEASE "${GCC_CLANG_COMMON_FLAGS_RELEASE}") elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") message(STATUS "MSVC detected - Adding compiler flags") - set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj") - set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG") +# set(CMAKE_CXX_FLAGS "/W3 /EHsc /bigobj") +# set(CMAKE_CXX_FLAGS_DEBUG "/MDd /Od /Zi /D_DEBUG /DDEBUG") +# set(CMAKE_CXX_FLAGS_RELEASE "/O1 /DNDEBUG") add_compile_options(/Zc:preprocessor) endif () #if(ENABLE_COVERAGE) diff --git a/include/matador/sql/connection_pool.hpp b/include/matador/sql/connection_pool.hpp new file mode 100644 index 0000000..c829250 --- /dev/null +++ b/include/matador/sql/connection_pool.hpp @@ -0,0 +1,188 @@ +#ifndef QUERY_CONNECTION_POOL_HPP +#define QUERY_CONNECTION_POOL_HPP + +#include "matador/sql/connection_info.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace matador::sql { + +template < class Connection > +class connection_pool; + +template < class Connection > +using IdConnection = std::pair; + +template < class Connection > +class connection_ptr +{ +public: + connection_ptr(IdConnection *c, connection_pool *pool) + : connection_(c), pool_(pool) {} + ~connection_ptr(); + connection_ptr(const connection_ptr &) = delete; + connection_ptr& operator=(const connection_ptr &) = delete; + connection_ptr(connection_ptr &&x) noexcept + : connection_(x.connection_) + , pool_(x.pool_) + { + x.connection_ = nullptr; + x.pool_ = nullptr; + } + connection_ptr& operator=(connection_ptr &&x) noexcept + { + if (this == &x) { + return *this; + } + + std::swap(connection_, x.connection_); + std::swap(pool_, x.pool_); + + return *this; + } + + Connection* operator->() { return &connection_->second; } + Connection& operator*() { return connection_->second; } + + [[nodiscard]] std::optional id() const + { + if (connection_) { + return connection_->first; + } else { + return std::nullopt; + } + } + [[nodiscard]] bool valid() const { return connection_ != nullptr; } + +private: + friend class connection_pool; + + IdConnection *connection_{}; + connection_pool *pool_{}; +}; + +template < class Connection > +class connection_pool +{ +public: + using connection_pointer = connection_ptr; + +public: + connection_pool(const std::string &dns, size_t count) + : info_(connection_info::parse(dns)) { + connection_repo_.reserve(count); + while (count) { + connection_repo_.emplace_back(count, info_); + auto &conn = connection_repo_.back(); + idle_connections_.emplace(conn.first, &conn); + conn.second.open(); + --count; + } + } + + connection_pointer acquire() { + std::unique_lock lock(mutex_); + while (idle_connections_.empty()) { + cv.wait(lock); + } + + return get_next_connection(); + } + + connection_pointer try_acquire() { + std::unique_lock lock(mutex_); + if (idle_connections_.empty()) { + return {nullptr, this}; + } + + return get_next_connection(); + } + + connection_pointer acquire(size_t id) { + using namespace std::chrono_literals; + pointer next_connection{nullptr}; + auto try_count{0}; + std::unique_lock lock(mutex_); + + do { + if (auto it = idle_connections_.find(id); it != idle_connections_.end()) { + next_connection = it->second; + auto node = idle_connections_.extract(it); + inuse_connections_.insert(std::move(node)); + } else { + lock.unlock(); + std::this_thread::sleep_for(100ms); + lock.lock(); + } + } while(try_count++ < 5); + + return {next_connection, this}; + } + + void release(IdConnection *c) { + if (c == nullptr) { + return; + } + std::unique_lock lock(mutex_); + if (auto it = inuse_connections_.find(c->first); it != inuse_connections_.end()) { + auto node = inuse_connections_.extract(it); + idle_connections_.insert(std::move(node)); + } + } + + void release(connection_ptr &c) { + release(c.connection_); + c.connection_ = nullptr; + } + + std::size_t size() const { return connection_repo_.size(); } + std::size_t idle() const { + std::lock_guard guard(mutex_); + return idle_connections_.size(); + } + std::size_t inuse() const { + std::lock_guard guard(mutex_); + return inuse_connections_.size(); + } + + const connection_info &info() const { + return info_; + } + +private: + connection_pointer get_next_connection() { + pointer next_connection{nullptr}; + for (auto &item : idle_connections_) { + next_connection = item.second; + auto node = idle_connections_.extract(item.first); + inuse_connections_.insert(std::move(node)); + break; + } + return {next_connection, this}; + } +private: + mutable std::mutex mutex_; + std::condition_variable cv; + std::vector> connection_repo_; + using pointer = IdConnection*; + using connection_map = std::unordered_map; + connection_map inuse_connections_; + connection_map idle_connections_; + + const connection_info info_; +}; + +template +connection_ptr::~connection_ptr() { + pool_->release(connection_); +} + +} + +#endif //QUERY_CONNECTION_POOL_HPP diff --git a/include/matador/sql/dialect.hpp b/include/matador/sql/dialect.hpp index df158a7..a81d88f 100644 --- a/include/matador/sql/dialect.hpp +++ b/include/matador/sql/dialect.hpp @@ -125,7 +125,7 @@ private: friend class dialect_builder; next_placeholder_func placeholder_func_ = [](size_t) { return "?"; }; - // to_escaped_string_func to_escaped_string_func_ = [](const utils::blob &val) { return utils::to_string(val); }; + to_escaped_string_func to_escaped_string_func_ = [](const utils::blob &val) { return utils::to_string(val); }; escape_identifier_t identifier_escape_type_ = escape_identifier_t::ESCAPE_BOTH_SAME; diff --git a/include/matador/sql/dialect_builder.hpp b/include/matador/sql/dialect_builder.hpp index f2425d4..84f6dc9 100644 --- a/include/matador/sql/dialect_builder.hpp +++ b/include/matador/sql/dialect_builder.hpp @@ -17,6 +17,7 @@ public: dialect_builder& with_placeholder_func(const dialect::next_placeholder_func &func); dialect_builder& with_default_schema_name(const std::string &schema_name); dialect_builder& with_bool_strings(const std::string &true_string, const std::string &false_string); + dialect_builder& with_escape_string_func(const dialect::to_escaped_string_func &func); dialect build(); diff --git a/include/matador/utils/placeholder.hpp b/include/matador/utils/placeholder.hpp index 2447c0a..90da9c3 100644 --- a/include/matador/utils/placeholder.hpp +++ b/include/matador/utils/placeholder.hpp @@ -5,7 +5,7 @@ namespace matador::utils { struct placeholder {}; -inline constexpr bool operator==(const placeholder&, const placeholder&) { return true; } +constexpr bool operator==(const placeholder&, const placeholder&) { return true; } static constexpr placeholder _; diff --git a/include/matador/utils/types.hpp b/include/matador/utils/types.hpp index 30202e6..58c31c4 100644 --- a/include/matador/utils/types.hpp +++ b/include/matador/utils/types.hpp @@ -18,6 +18,7 @@ using database_type = std::variant< int8_t, int16_t, int32_t, int64_t, float, double, bool, + const char*, std::string, blob, nullptr_t>; diff --git a/source/orm/query/query_update_intermediate.cpp b/source/orm/query/query_update_intermediate.cpp index 155cab7..06e39fd 100644 --- a/source/orm/query/query_update_intermediate.cpp +++ b/source/orm/query/query_update_intermediate.cpp @@ -9,7 +9,7 @@ query_update_intermediate::query_update_intermediate(const sql::table& table) context_->parts.push_back(std::make_unique(table)); } -query_set_intermediate query_update_intermediate::set(std::initializer_list columns) +query_set_intermediate query_update_intermediate::set(const std::initializer_list columns) { return set(std::vector{columns}); } diff --git a/source/orm/sql/dialect.cpp b/source/orm/sql/dialect.cpp index 094bc26..96c5e57 100644 --- a/source/orm/sql/dialect.cpp +++ b/source/orm/sql/dialect.cpp @@ -124,8 +124,7 @@ std::string dialect::to_escaped_string(const utils::blob &value, const connectio return conn->to_escaped_string(value); } - return {}; - // return to_escaped_string_func_(value); + return to_escaped_string_func_(value); } std::string dialect::default_schema_name() const diff --git a/source/orm/sql/dialect_builder.cpp b/source/orm/sql/dialect_builder.cpp index 999e7a7..648ed02 100644 --- a/source/orm/sql/dialect_builder.cpp +++ b/source/orm/sql/dialect_builder.cpp @@ -52,6 +52,12 @@ dialect_builder &dialect_builder::with_bool_strings(const std::string &true_stri return *this; } +dialect_builder& dialect_builder::with_escape_string_func( const dialect::to_escaped_string_func& func ) { + dialect_.to_escaped_string_func_ = func; + + return *this; +} + dialect dialect_builder::build() { return std::move(dialect_); diff --git a/source/orm/sql/interface/query_result_reader.cpp b/source/orm/sql/interface/query_result_reader.cpp index cf457b0..9b3e474 100644 --- a/source/orm/sql/interface/query_result_reader.cpp +++ b/source/orm/sql/interface/query_result_reader.cpp @@ -1,8 +1,6 @@ #include "matador/sql/interface/query_result_reader.hpp" -#include "matador/utils/basic_types.hpp" #include "matador/utils/convert.hpp" -#include "matador/utils/string.hpp" #include "matador/utils/value.hpp" namespace matador::sql { @@ -19,20 +17,4 @@ void convert(const char *val_str, utils::value &val) val = *res; } -// utils::blob query_result_reader::read_blob(const size_t index) -// { -// const auto *data = column(index); -// if (data == nullptr) { -// return {}; -// } -// const auto len = strlen(data); -// const auto *bytes = reinterpret_cast(data); -// return utils::blob{bytes, bytes+len}; -// } - -// utils::attribute_reader &query_result_reader::result_binder() -// { - // return empty_result_binder_; -// } - } \ No newline at end of file diff --git a/test/orm/CMakeLists.txt b/test/orm/CMakeLists.txt index 3ebfee5..96b0303 100644 --- a/test/orm/CMakeLists.txt +++ b/test/orm/CMakeLists.txt @@ -7,19 +7,22 @@ add_executable(OrmTests backend/test_backend_service.hpp backend/test_connection.cpp backend/test_connection.hpp - query/ConditionTests.cpp - query/QueryBuilderTest.cpp - query/QueryFixture.cpp - query/QueryFixture.hpp - sql/ColumnTest.cpp - sql/FieldTest.cpp + backend/test_parameter_binder.cpp + backend/test_parameter_binder.hpp backend/test_result_reader.cpp backend/test_result_reader.hpp backend/test_statement.cpp backend/test_statement.hpp - backend/test_parameter_binder.cpp - backend/test_parameter_binder.hpp + query/ConditionTests.cpp + query/QueryBuilderTest.cpp + query/QueryFixture.cpp + query/QueryFixture.hpp query/QueryTest.cpp + sql/ColumnTest.cpp + sql/ConnectionPoolTest.cpp + sql/FieldTest.cpp + utils/auto_reset_event.cpp + utils/auto_reset_event.hpp ) target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain) diff --git a/test/orm/backend/test_backend_service.cpp b/test/orm/backend/test_backend_service.cpp index 729e627..dbac91b 100644 --- a/test/orm/backend/test_backend_service.cpp +++ b/test/orm/backend/test_backend_service.cpp @@ -24,7 +24,9 @@ void test_backend_service::destroy(sql::connection_impl *impl) const sql::dialect *test_backend_service::dialect() const { - static sql::dialect dialect_ = sql::dialect_builder::builder().create().build(); + static sql::dialect dialect_ = sql::dialect_builder::builder() + .create() + .build(); return &dialect_; } diff --git a/test/orm/backend/test_result_reader.cpp b/test/orm/backend/test_result_reader.cpp index 8498cff..267c8ff 100644 --- a/test/orm/backend/test_result_reader.cpp +++ b/test/orm/backend/test_result_reader.cpp @@ -87,6 +87,7 @@ void test_result_reader::read_value(const char *id, const size_t index, utils::v val = "value"; } utils::attribute_reader &test_result_reader::result_binder() { - return query_result_reader::result_binder(); + return empty_binder_; } + } // namespace matador::test::orm diff --git a/test/orm/backend/test_result_reader.hpp b/test/orm/backend/test_result_reader.hpp index 09f1cdd..c9e9b46 100644 --- a/test/orm/backend/test_result_reader.hpp +++ b/test/orm/backend/test_result_reader.hpp @@ -5,6 +5,33 @@ namespace matador::test::orm { +namespace detail { + +class empty_binder final : public utils::attribute_reader +{ +public: + void read_value(const char *, size_t, int8_t &) override {} + void read_value(const char *, size_t, int16_t &) override {} + void read_value(const char *, size_t, int32_t &) override {} + void read_value(const char *, size_t, int64_t &) override {} + void read_value(const char *, size_t, uint8_t &) override {} + void read_value(const char *, size_t, uint16_t &) override {} + void read_value(const char *, size_t, uint32_t &) override {} + void read_value(const char *, size_t, uint64_t &) override {} + void read_value(const char *, size_t, bool &) override {} + void read_value(const char *, size_t, float &) override {} + void read_value(const char *, size_t, double &) override {} + void read_value(const char *, size_t, time &) override {} + void read_value(const char *, size_t, date &) override {} + void read_value(const char *, size_t, char *, size_t) override {} + void read_value(const char *, size_t, std::string &) override {} + void read_value(const char *, size_t, std::string &, size_t) override {} + void read_value(const char *, size_t, utils::blob &) override {} + void read_value(const char *, size_t, utils::value &, size_t) override {} +}; + +} + class test_result_reader final : public sql::query_result_reader { public: [[nodiscard]] size_t column_count() const override; @@ -36,6 +63,7 @@ protected: private: uint8_t rows_{5}; + detail::empty_binder empty_binder_; }; } diff --git a/test/orm/query/QueryBuilderTest.cpp b/test/orm/query/QueryBuilderTest.cpp index b9fcc4b..be48ef2 100644 --- a/test/orm/query/QueryBuilderTest.cpp +++ b/test/orm/query/QueryBuilderTest.cpp @@ -22,7 +22,7 @@ using namespace matador::utils; TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query]") { auto result = query::create() .table({"person"}, { - make_pk_column("id"), + make_pk_column("id"), make_column("name", 255), make_column("age") }).str(*db); @@ -31,10 +31,10 @@ TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query result = query::create() .table("person", { - make_pk_column("id"), + make_pk_column("id"), make_column("name", {255, constraints::UNIQUE}, null_option::NOT_NULL), make_column("age"), - make_fk_column("address", "address", "id") + make_fk_column("address", "address", "id") }).str(*db); REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL UNIQUE, "age" INTEGER NOT NULL, "address" BIGINT NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id), CONSTRAINT FK_person_address FOREIGN KEY (address) REFERENCES address(id)))##"); @@ -61,7 +61,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert sql statement string", "[query]") { .into("person", { "id", "name", "age" }) - .values({7UL, "george", 65U}) + .values({7U, "george", 65U}) .str(*db); REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))"); @@ -70,7 +70,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert sql statement string", "[query]") { TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") { auto result = query::update("person") .set({ - {"id", 7UL}, + {"id", 7U}, {"name", "george"}, {"age", 65U} }) @@ -80,7 +80,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") { result = query::update("person") .set({ - {"id", 7UL}, + {"id", 7U}, {"name", "george"}, {"age", 65U} }) @@ -95,7 +95,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") { TEST_CASE_METHOD(QueryFixture, "Test update limit sql statement", "[query][update][limit]") { const auto result = query::update("person") - .set({{"id", 7UL}, {"name", "george"}, {"age", 65U}}) + .set({{"id", 7U}, {"name", "george"}, {"age", 65U}}) .where("name"_col == "george") .order_by("id"_col).asc() .limit(2) @@ -185,7 +185,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with offset and TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", "[query][blob]") { auto result = query::create() .table("person", { - make_pk_column("id"), + make_pk_column("id"), make_column("name", 255), make_column("data") }) @@ -195,7 +195,7 @@ TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", " result = query::insert() .into("person", {"id", "name", "data"}) - .values({7UL, "george", blob{1, 'A', 3, 4}}) + .values({7U, "george", blob{1, 'A', 3, 4}}) .str(*db); REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))"); diff --git a/test/orm/sql/ColumnTest.cpp b/test/orm/sql/ColumnTest.cpp index 341b31d..fce5fe5 100644 --- a/test/orm/sql/ColumnTest.cpp +++ b/test/orm/sql/ColumnTest.cpp @@ -21,7 +21,7 @@ TEST_CASE("Test create empty column", "[column]") { c.set(7); REQUIRE(c.type() == basic_type::type_int32); REQUIRE(c.as() == "7"); - REQUIRE(c.as() == 7); + REQUIRE(c.as() == 7); REQUIRE(c.str() == "7"); } diff --git a/test/orm/sql/ConnectionPoolTest.cpp b/test/orm/sql/ConnectionPoolTest.cpp new file mode 100644 index 0000000..c313407 --- /dev/null +++ b/test/orm/sql/ConnectionPoolTest.cpp @@ -0,0 +1,176 @@ +#include + +#include "matador/sql/backend_provider.hpp" +#include "matador/sql/connection.hpp" +#include "matador/sql/connection_pool.hpp" + +#include "../backend/test_connection.hpp" +#include "../backend/test_backend_service.hpp" + +#include "../utils/auto_reset_event.hpp" + +using namespace matador::sql; +using namespace matador::test::utils; +using namespace matador::test::orm; + +namespace matador::test::orm { + +class ConnectionPoolFixture { +public: + ConnectionPoolFixture() { + backend_provider::instance().register_backend("noop", std::make_unique()); + + db = std::make_unique("noop://noop.db"); + } + ~ConnectionPoolFixture() = default; + +protected: + std::unique_ptr db; +}; + +} + +TEST_CASE_METHOD(ConnectionPoolFixture, "Create connection pool", "[connection pool]") { + using pool_t = connection_pool; + + pool_t pool("noop://noop.db", 4); + + REQUIRE(pool.size() == 4); + REQUIRE(pool.idle() == 4); + REQUIRE(pool.inuse() == 0); + + auto ptr = pool.acquire(); + REQUIRE(ptr.valid()); + REQUIRE(ptr.id().value() > 0); + REQUIRE(ptr->is_open()); + + REQUIRE(pool.idle() == 3); + REQUIRE(pool.inuse() == 1); + + pool.release(ptr); + REQUIRE(!ptr.valid()); + REQUIRE(pool.idle() == 4); + REQUIRE(pool.inuse() == 0); + + ptr = pool.acquire(3); + REQUIRE(ptr.valid()); + REQUIRE(ptr.id() == 3); + REQUIRE(ptr->is_open()); + { + auto ptr2 = pool.acquire(); + REQUIRE(ptr2.valid()); + REQUIRE(ptr2->is_open()); + + REQUIRE(pool.idle() == 2); + REQUIRE(pool.inuse() == 2); + } + + REQUIRE(pool.idle() == 3); + REQUIRE(pool.inuse() == 1); + + pool.release(ptr); + REQUIRE(!ptr.valid()); + REQUIRE(pool.idle() == 4); + REQUIRE(pool.inuse() == 0); +} + +TEST_CASE_METHOD(ConnectionPoolFixture, "Acquire connection by id", "[connection pool]") { + using pool_t = connection_pool; + + pool_t pool("noop://noop.db", 4); + + REQUIRE(pool.size() == 4); + REQUIRE(pool.idle() == 4); + REQUIRE(pool.inuse() == 0); + + auto ptr = pool.acquire(); + REQUIRE(ptr.valid()); + REQUIRE(ptr.id()); + REQUIRE(ptr.id().value() > 0); + REQUIRE(ptr->is_open()); + + auto same_ptr = pool.acquire(ptr.id().value()); + + REQUIRE(!same_ptr.valid()); + + const auto connection_id = ptr.id().value(); + + pool.release(ptr); + REQUIRE(!ptr.valid()); + + same_ptr = pool.acquire(connection_id); + + REQUIRE(same_ptr.valid()); + REQUIRE(same_ptr.id() == connection_id); +} + +TEST_CASE("Try acquire connection", "[connection pool][try acquire]") { + using pool_t = connection_pool; + + pool_t pool("noop://noop.db", 1); + + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 1); + REQUIRE(pool.inuse() == 0); + + auto ptr = pool.try_acquire(); + REQUIRE(ptr.valid()); + REQUIRE(ptr.id()); + REQUIRE(ptr.id().value() > 0); + REQUIRE(ptr->is_open()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 0); + REQUIRE(pool.inuse() == 1); + + auto ptr2 = pool.try_acquire(); + REQUIRE(!ptr2.valid()); + + pool.release(ptr); + REQUIRE(!ptr.valid()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 1); + REQUIRE(pool.inuse() == 0); + + ptr2 = pool.try_acquire(); + REQUIRE(ptr2.valid()); + REQUIRE(ptr2.id()); + REQUIRE(ptr2.id().value() > 0); + REQUIRE(ptr2->is_open()); + REQUIRE(pool.size() == 1); + REQUIRE(pool.idle() == 0); + REQUIRE(pool.inuse() == 1); + + pool.release(ptr2); + + auto_reset_event reset_main_event; + auto_reset_event reset_thread_event; + + std::thread t([&reset_main_event, &reset_thread_event, &pool]() { + auto c1 = pool.acquire(); + REQUIRE(c1.valid()); + REQUIRE(c1.id()); + REQUIRE(c1.id().value() > 0); + + reset_main_event.set(); + + reset_thread_event.wait_one(); + + pool.release(c1); + REQUIRE(!c1.valid()); + reset_main_event.set(); + }); + reset_main_event.wait_one(); + + ptr2 = pool.try_acquire(); + REQUIRE(!ptr2.valid()); + + reset_thread_event.set(); + + reset_main_event.wait_one(); + ptr2 = pool.try_acquire(); + REQUIRE(ptr2.valid()); + REQUIRE(ptr2.id()); + REQUIRE(ptr2.id().value() > 0); + + t.join(); +} \ No newline at end of file diff --git a/test/orm/sql/FieldTest.cpp b/test/orm/sql/FieldTest.cpp index ffee8d3..7e93fcb 100644 --- a/test/orm/sql/FieldTest.cpp +++ b/test/orm/sql/FieldTest.cpp @@ -16,7 +16,7 @@ TEST_CASE("Test field", "[field]") { REQUIRE(!f.is_bool()); REQUIRE(!f.is_string()); - f = 7UL; + f = 7U; REQUIRE(!f.is_null()); REQUIRE(f.is_integer()); REQUIRE(!f.is_floating_point()); diff --git a/test/orm/utils/auto_reset_event.cpp b/test/orm/utils/auto_reset_event.cpp new file mode 100644 index 0000000..12d632e --- /dev/null +++ b/test/orm/utils/auto_reset_event.cpp @@ -0,0 +1,27 @@ +#include "auto_reset_event.hpp" + +namespace matador::test::utils { + +auto_reset_event::auto_reset_event() : state(false) {} + +void auto_reset_event::wait_one() +{ + std::unique_lock lock(sync); + underlying.wait(lock, [this](){return state.load();}); + state = false; +} + +void auto_reset_event::set() +{ + std::unique_lock lock(sync); + state = true; + underlying.notify_one(); +} + +void auto_reset_event::reset() +{ + std::unique_lock lock(sync); + state = false; +} + +} \ No newline at end of file diff --git a/test/orm/utils/auto_reset_event.hpp b/test/orm/utils/auto_reset_event.hpp new file mode 100644 index 0000000..ebbb30d --- /dev/null +++ b/test/orm/utils/auto_reset_event.hpp @@ -0,0 +1,28 @@ +#ifndef QUERY_AUTO_RESET_EVENT_HPP +#define QUERY_AUTO_RESET_EVENT_HPP + +#include +#include +#include + +namespace matador::test::utils { + +class auto_reset_event +{ +public: + auto_reset_event(); + auto_reset_event(const auto_reset_event& other) = delete; + + void wait_one(); + void set(); + void reset(); + +private: + std::condition_variable underlying; + std::mutex sync; + std::atomic state; +}; + +} + +#endif //QUERY_AUTO_RESET_EVENT_HPP