added connection pool and tests

This commit is contained in:
Sascha Kühl
2025-02-05 15:47:51 +01:00
parent 45a7199ccf
commit bc3ffbda10
20 changed files with 495 additions and 53 deletions
+11 -8
View File
@@ -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)
+3 -1
View File
@@ -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_;
}
+2 -1
View File
@@ -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
+28
View File
@@ -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_;
};
}
+9 -9
View File
@@ -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<unsigned long>("id"),
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", 255),
make_column<unsigned short>("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<unsigned long>("id"),
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", {255, constraints::UNIQUE}, null_option::NOT_NULL),
make_column<unsigned short>("age"),
make_fk_column<unsigned long>("address", "address", "id")
make_fk_column<uint32_t>("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<unsigned long>("id"),
make_pk_column<uint32_t>("id"),
make_column<std::string>("name", 255),
make_column<blob>("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'))");
+1 -1
View File
@@ -21,7 +21,7 @@ TEST_CASE("Test create empty column", "[column]") {
c.set(7);
REQUIRE(c.type() == basic_type::type_int32);
REQUIRE(c.as<std::string>() == "7");
REQUIRE(c.as<long>() == 7);
REQUIRE(c.as<int>() == 7);
REQUIRE(c.str() == "7");
}
+176
View File
@@ -0,0 +1,176 @@
#include <catch2/catch_test_macros.hpp>
#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<test_backend_service>());
db = std::make_unique<sql::connection>("noop://noop.db");
}
~ConnectionPoolFixture() = default;
protected:
std::unique_ptr<connection> db;
};
}
TEST_CASE_METHOD(ConnectionPoolFixture, "Create connection pool", "[connection pool]") {
using pool_t = connection_pool<test_connection>;
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<test_connection>;
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<test_connection>;
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();
}
+1 -1
View File
@@ -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());
+27
View File
@@ -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<std::mutex> lock(sync);
underlying.wait(lock, [this](){return state.load();});
state = false;
}
void auto_reset_event::set()
{
std::unique_lock<std::mutex> lock(sync);
state = true;
underlying.notify_one();
}
void auto_reset_event::reset()
{
std::unique_lock<std::mutex> lock(sync);
state = false;
}
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef QUERY_AUTO_RESET_EVENT_HPP
#define QUERY_AUTO_RESET_EVENT_HPP
#include <atomic>
#include <condition_variable>
#include <mutex>
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<bool> state;
};
}
#endif //QUERY_AUTO_RESET_EVENT_HPP