added connection pool and tests
This commit is contained in:
+11
-8
@@ -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)
|
||||
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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'))");
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user