From 843d125b9923ac4105e3221ef05ff0cf9263254f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Thu, 14 Aug 2025 15:48:21 +0200 Subject: [PATCH] added session context and implemented the executor interface for session --- include/matador/orm/error_code.hpp | 3 +- include/matador/orm/session.hpp | 94 ++++++++++--------- .../query/intermediates/executable_query.hpp | 3 +- .../query/intermediates/fetchable_query.hpp | 3 +- include/matador/sql/connection.hpp | 2 +- include/matador/sql/executor.hpp | 15 +-- include/matador/sql/statement.hpp | 2 + include/matador/sql/statement_cache.hpp | 1 + source/orm/orm/error_code.cpp | 2 + source/orm/orm/session.cpp | 74 +++++++++------ .../query/intermediates/executable_query.cpp | 2 +- .../query/intermediates/fetchable_query.cpp | 2 +- source/orm/sql/statement.cpp | 10 ++ source/orm/sql/statement_cache.cpp | 3 + test/backends/SessionFixture.cpp | 2 +- test/backends/SessionFixture.hpp | 3 + test/backends/SessionTest.cpp | 3 - test/backends/StatementCacheTest.cpp | 4 +- test/orm/backend/test_statement.cpp | 3 +- 19 files changed, 131 insertions(+), 100 deletions(-) diff --git a/include/matador/orm/error_code.hpp b/include/matador/orm/error_code.hpp index 69e396f..ab3461c 100644 --- a/include/matador/orm/error_code.hpp +++ b/include/matador/orm/error_code.hpp @@ -11,7 +11,8 @@ enum class error_code : uint8_t { NoConnectionAvailable, UnknownType, FailedToBuildQuery, - FailedToFindObject + FailedToFindObject, + Failed }; class orm_category_impl final : public std::error_category diff --git a/include/matador/orm/session.hpp b/include/matador/orm/session.hpp index 5ecde7f..a31eb01 100644 --- a/include/matador/orm/session.hpp +++ b/include/matador/orm/session.hpp @@ -9,7 +9,9 @@ #include "matador/sql/column_generator.hpp" #include "matador/sql/connection.hpp" #include "matador/sql/connection_pool.hpp" +#include "matador/sql/executor.hpp" #include "matador/sql/statement.hpp" +#include "matador/sql/statement_cache.hpp" #include "matador/object/object_ptr.hpp" #include "matador/object/object_definition.hpp" @@ -21,9 +23,15 @@ namespace matador::orm { utils::error make_error(error_code ec, const std::string &msg); -class session final { +struct session_context { + utils::message_bus &bus; + sql::connection_pool &pool; + size_t cache_size{500}; +}; + +class session final : public sql::executor { public: - explicit session(sql::connection_pool &pool); + explicit session(session_context &&ctx); template [[nodiscard]] utils::result attach(const std::string &table_name) const; @@ -42,10 +50,10 @@ public: template utils::result, utils::error> find(const PrimaryKeyType &pk) { - auto c = pool_.acquire(); - if (!c.valid()) { - return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); - } + // auto c = pool_.acquire(); + // if (!c.valid()) { + // return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); + // } auto info = schema_->info(); if (!info) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); @@ -57,7 +65,7 @@ public: return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); } - auto obj = build_select_query(data.release()).template fetch_one(*c); + auto obj = build_select_query(data.release()).template fetch_one(*this); if (!obj) { return utils::failure(obj.err()); @@ -70,10 +78,10 @@ public: template utils::result, utils::error> find() { - const auto c = pool_.acquire(); - if (!c.valid()) { - return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); - } + // const auto c = pool_.acquire(); + // if (!c.valid()) { + // return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); + // } auto info = schema_->info(); if (!info) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); @@ -85,51 +93,54 @@ public: return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); } - return build_select_query(data.release()).template fetch_all(*c); - } - - template - utils::result select() { - auto c = pool_.acquire(); - if (!c.valid()) { - return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); - } - auto info = schema_->info(); - if (!info) { - return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); - } - - session_query_builder eqb(*schema_); - auto data = eqb.build(); - if (!data.is_ok()) { - return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); - } - - return utils::ok(build_select_query(data.release()).template fetch_all(*c)); + return build_select_query(data.release()).template fetch_all(*this); } + // template + // utils::result select() { + // auto c = pool_.acquire(); + // if (!c.valid()) { + // return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); + // } + // auto info = schema_->info(); + // if (!info) { + // return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); + // } + // + // session_query_builder eqb(*schema_); + // auto data = eqb.build(); + // if (!data.is_ok()) { + // return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info->get().name() + ".")); + // } + // + // return utils::ok(build_select_query(data.release()).template fetch_all(*c)); + // } + // template utils::result drop_table(); utils::result drop_table(const std::string &table_name) const; - [[nodiscard]] utils::result, utils::error> fetch(const sql::query_context &q) const; - [[nodiscard]] size_t execute(const std::string &sql) const; - [[nodiscard]] sql::statement prepare(const sql::query_context& q) const; + [[nodiscard]] utils::result, utils::error> fetch_all(const sql::query_context &q) const; + [[nodiscard]] utils::result execute(const std::string &sql) const; [[nodiscard]] std::vector describe_table(const std::string &table_name) const; [[nodiscard]] bool table_exists(const std::string &table_name) const; - [[nodiscard]] const sql::dialect& dialect() const; - void dump_schema(std::ostream &os) const; + [[nodiscard]] utils::result, utils::error> fetch(const sql::query_context &ctx) const override; + [[nodiscard]] utils::result execute(const sql::query_context &ctx) const override; + [[nodiscard]] utils::result prepare(const sql::query_context &ctx) override; + [[nodiscard]] std::string str( const sql::query_context& ctx ) const override; + [[nodiscard]] const sql::dialect& dialect() const override; + private: friend class query_select; static query::fetchable_query build_select_query(entity_query_data &&data); private: - sql::connection_pool &pool_; + mutable sql::statement_cache cache_; const sql::dialect &dialect_; std::unique_ptr schema_; @@ -147,9 +158,8 @@ utils::result session::attach( const std::string& table_name } template -utils::result, utils::error> session::insert(Type *obj) -{ - auto c = pool_.acquire(); +utils::result, utils::error> session::insert(Type *obj) { + // auto c = pool_.acquire(); auto info = schema_->info(); if (!info) { return utils::failure(info.err()); @@ -158,7 +168,7 @@ utils::result, utils::error> session::insert(Type *obj) auto res = query::query::insert() .into(info->get().name(), sql::column_generator::generate(*schema_, true)) .values(*obj) - .execute(*c); + .execute(*this); if (!res) { return utils::failure(res.err()); } diff --git a/include/matador/query/intermediates/executable_query.hpp b/include/matador/query/intermediates/executable_query.hpp index 2223f2e..b4d513e 100644 --- a/include/matador/query/intermediates/executable_query.hpp +++ b/include/matador/query/intermediates/executable_query.hpp @@ -8,7 +8,6 @@ namespace matador::sql { class executor; -class prepared_executor; class statement; struct query_context; } @@ -20,7 +19,7 @@ public: using query_intermediate::query_intermediate; [[nodiscard]] utils::result execute(const sql::executor &exec) const; - [[nodiscard]] utils::result prepare(sql::prepared_executor &exec) const; + [[nodiscard]] utils::result prepare(sql::executor &exec) const; [[nodiscard]] sql::query_context compile(const sql::executor &exec) const; [[nodiscard]] std::string str(const sql::executor &exec) const; }; diff --git a/include/matador/query/intermediates/fetchable_query.hpp b/include/matador/query/intermediates/fetchable_query.hpp index 94b783a..7aa1f88 100644 --- a/include/matador/query/intermediates/fetchable_query.hpp +++ b/include/matador/query/intermediates/fetchable_query.hpp @@ -13,7 +13,6 @@ namespace matador::sql { class executor; -class prepared_executor; class statement; } @@ -69,7 +68,7 @@ public: return utils::ok(std::optional{std::nullopt}); } - [[nodiscard]] utils::result prepare(sql::prepared_executor &exec) const; + [[nodiscard]] utils::result prepare(sql::executor &exec) const; [[nodiscard]] std::string str(const sql::executor &exec) const; diff --git a/include/matador/sql/connection.hpp b/include/matador/sql/connection.hpp index c9bb92a..73c1c80 100644 --- a/include/matador/sql/connection.hpp +++ b/include/matador/sql/connection.hpp @@ -16,7 +16,7 @@ class connection_impl; /** * @brief The connection class represents a connection to a database. */ -class connection final : public executor, public prepared_executor { +class connection final : public executor { public: using logger_ptr = std::shared_ptr; /** diff --git a/include/matador/sql/executor.hpp b/include/matador/sql/executor.hpp index 9734531..94ddb4a 100644 --- a/include/matador/sql/executor.hpp +++ b/include/matador/sql/executor.hpp @@ -12,22 +12,13 @@ struct query_context; class query_result_impl; class statement; -class dialect_executor_mixin { +class executor { public: - virtual ~dialect_executor_mixin() = default; + virtual ~executor() = default; [[nodiscard]] virtual const class dialect& dialect() const = 0; -}; - -class prepared_executor : public dialect_executor_mixin { -public: - [[nodiscard]] virtual utils::result prepare(const query_context &ctx) = 0; -}; - -class executor : public dialect_executor_mixin { -public: [[nodiscard]] virtual utils::result execute(const query_context &ctx) const = 0; [[nodiscard]] virtual utils::result, utils::error> fetch(const query_context &ctx) const = 0; - + [[nodiscard]] virtual utils::result prepare(const query_context &ctx) = 0; [[nodiscard]] virtual std::string str(const query_context &ctx) const = 0; }; diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 153b29a..75c6c2c 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -123,6 +123,8 @@ public: [[nodiscard]] std::string sql() const; + [[nodiscard]] utils::result, utils::error> fetch_internal() const; + private: template friend class detail::identifier_binder; diff --git a/include/matador/sql/statement_cache.hpp b/include/matador/sql/statement_cache.hpp index 681ba4f..a79cc88 100644 --- a/include/matador/sql/statement_cache.hpp +++ b/include/matador/sql/statement_cache.hpp @@ -68,6 +68,7 @@ public: return bus_.subscribe(handler); } + connection_pool& pool() const; private: size_t max_size_{}; diff --git a/source/orm/orm/error_code.cpp b/source/orm/orm/error_code.cpp index febd8d9..0044642 100644 --- a/source/orm/orm/error_code.cpp +++ b/source/orm/orm/error_code.cpp @@ -18,6 +18,8 @@ std::string orm_category_impl::message(const int ev) const { return "Failed to build query"; case error_code::FailedToFindObject: return "Failed to find object"; + case error_code::Failed: + return "Failed"; default: return "Unknown error"; } diff --git a/source/orm/orm/session.cpp b/source/orm/orm/session.cpp index d5778d8..9d56e4c 100644 --- a/source/orm/orm/session.cpp +++ b/source/orm/orm/session.cpp @@ -12,9 +12,9 @@ utils::error make_error(const error_code ec, const std::string &msg) { return utils::error(ec, msg); } -session::session(sql::connection_pool &pool) -: pool_(pool) -, dialect_(sql::backend_provider::instance().connection_dialect(pool_.info().type)) +session::session(session_context&& ctx) +: cache_(ctx.bus, ctx.pool, ctx.cache_size) +, dialect_(sql::backend_provider::instance().connection_dialect(ctx.pool.info().type)) , schema_(std::make_unique(dialect_.default_schema_name())) { } @@ -49,7 +49,7 @@ utils::result session::create_schema() const { } std::vector fk_sql_commands; - auto c = pool_.acquire(); + auto c = cache_.pool().acquire(); for (const auto &node: *schema_) { auto ctx = query::query::create() .table(node->name(), node->info().definition().columns()) @@ -75,14 +75,9 @@ utils::result session::create_schema() const { } utils::result session::drop_table(const std::string &table_name) const { - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - auto result = query::query::drop() .table(table_name) - .execute(*c); + .execute(*this); if (result.is_error()) { return utils::failure(result.err()); } @@ -90,10 +85,10 @@ utils::result session::drop_table(const std::string &table_n return utils::ok(); } -utils::result, utils::error> session::fetch(const sql::query_context &q) const { - auto c = pool_.acquire(); +utils::result, utils::error> session::fetch_all(const sql::query_context &q) const { + auto c = cache_.pool().acquire(); if (!c.valid()) { - throw std::logic_error("no database connection available"); + return utils::failure(make_error(error_code::NoConnectionAvailable, "Failed to acquire connection.")); } auto it = prototypes_.find(q.table.name); if (it == prototypes_.end()) { @@ -109,28 +104,19 @@ utils::result, utils::error> session::fetch(const const_cast(col).type(rit->type()); } } - auto res = c->fetch(q); + auto res = fetch(q); + if (!res) { + return utils::failure(res.err()); + } return utils::ok(sql::query_result{std::move(*res)}); } -size_t session::execute(const std::string &sql) const { - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->execute(sql); -} - -sql::statement session::prepare(const sql::query_context &q) const { - auto c = pool_.acquire(); - if (!c.valid()) { - throw std::logic_error("no database connection available"); - } - return c->prepare(q).release(); +utils::result session::execute(const std::string &sql) const { + return execute(sql::query_context{sql}); } std::vector session::describe_table(const std::string &table_name) const { - auto c = pool_.acquire(); + const auto c = cache_.pool().acquire(); if (!c.valid()) { throw std::logic_error("no database connection available"); } @@ -138,7 +124,7 @@ std::vector session::describe_table(const std::str } bool session::table_exists(const std::string &table_name) const { - auto c = pool_.acquire(); + const auto c = cache_.pool().acquire(); if (!c.valid()) { throw std::logic_error("no database connection available"); } @@ -153,6 +139,34 @@ void session::dump_schema(std::ostream &os) const { schema_->dump(os); } +utils::result, utils::error> session::fetch(const sql::query_context& ctx) const { + if (const auto result = cache_.acquire(ctx); !result) { + return utils::failure(result.err()); + } else if (auto fetch_result = result->fetch_internal(); !fetch_result) { + return utils::failure(fetch_result.err()); + } else { + return fetch_result; + } +} + +utils::result session::execute(const sql::query_context& ctx) const { + if (const auto result = cache_.acquire(ctx); !result) { + return utils::failure(result.err()); + } else if (auto exec_result = result->execute(); !exec_result) { + return utils::failure(exec_result.err()); + } else { + return exec_result; + } +} + +utils::result session::prepare(const sql::query_context& ctx) { + return cache_.acquire(ctx); +} + +std::string session::str(const sql::query_context& ctx) const { + return ctx.sql; +} + query::fetchable_query session::build_select_query(entity_query_data &&data) { return query::query::select(data.columns) .from(*data.root_table) diff --git a/source/orm/query/intermediates/executable_query.cpp b/source/orm/query/intermediates/executable_query.cpp index 005588b..30329fb 100644 --- a/source/orm/query/intermediates/executable_query.cpp +++ b/source/orm/query/intermediates/executable_query.cpp @@ -12,7 +12,7 @@ utils::result executable_query::execute(const sql::executo return exec.execute(compiler.compile(*context_, exec.dialect(), std::nullopt)); } -utils::result executable_query::prepare(sql::prepared_executor &exec) const { +utils::result executable_query::prepare(sql::executor &exec) const { query_compiler compiler; context_->mode = query_mode::Prepared; return exec.prepare(compiler.compile(*context_, exec.dialect(), std::nullopt)); diff --git a/source/orm/query/intermediates/fetchable_query.cpp b/source/orm/query/intermediates/fetchable_query.cpp index 6a704f7..04744dd 100644 --- a/source/orm/query/intermediates/fetchable_query.cpp +++ b/source/orm/query/intermediates/fetchable_query.cpp @@ -43,7 +43,7 @@ utils::result, utils::error> fetchable_q return exec.fetch(compiler.compile(*context_, exec.dialect(), std::nullopt)); } -utils::result fetchable_query::prepare(sql::prepared_executor &exec) const { +utils::result fetchable_query::prepare(sql::executor &exec) const { query_compiler compiler; context_->mode = query_mode::Prepared; return exec.prepare(compiler.compile(*context_, exec.dialect(), std::nullopt)); diff --git a/source/orm/sql/statement.cpp b/source/orm/sql/statement.cpp index 13595e2..b3a2754 100644 --- a/source/orm/sql/statement.cpp +++ b/source/orm/sql/statement.cpp @@ -98,4 +98,14 @@ void statement::reset() const std::string statement::sql() const { return statement_proxy_->sql(); } + +utils::result, utils::error> statement::fetch_internal() const { + auto result = statement_proxy_->fetch(*bindings_); + if (!result.is_ok()) { + return utils::failure(result.err()); + } + + // logger_.info(statement_->query_.sql); + return result; +} } diff --git a/source/orm/sql/statement_cache.cpp b/source/orm/sql/statement_cache.cpp index ae8b83b..f10727b 100644 --- a/source/orm/sql/statement_cache.cpp +++ b/source/orm/sql/statement_cache.cpp @@ -205,4 +205,7 @@ bool statement_cache::empty() const { return cache_map_.empty(); } +connection_pool& statement_cache::pool() const { + return pool_; +} } diff --git a/test/backends/SessionFixture.cpp b/test/backends/SessionFixture.cpp index 2fe89a6..dce6cf3 100644 --- a/test/backends/SessionFixture.cpp +++ b/test/backends/SessionFixture.cpp @@ -5,7 +5,7 @@ namespace matador::test { SessionFixture::SessionFixture() - : pool(connection::dns, 4), ses(pool) {} + : pool(connection::dns, 4), ses({bus, pool}) {} SessionFixture::~SessionFixture() { while (!tables_to_drop.empty()) { diff --git a/test/backends/SessionFixture.hpp b/test/backends/SessionFixture.hpp index f963faf..b0e6370 100644 --- a/test/backends/SessionFixture.hpp +++ b/test/backends/SessionFixture.hpp @@ -3,6 +3,8 @@ #include "matador/orm/session.hpp" +#include "matador/utils/message_bus.hpp" + #include "connection.hpp" #include @@ -18,6 +20,7 @@ protected: sql::connection_pool pool; std::stack tables_to_drop; orm::session ses; + utils::message_bus bus; private: void drop_table_if_exists(const std::string &table_name) const; diff --git a/test/backends/SessionTest.cpp b/test/backends/SessionTest.cpp index a2df001..f0621a5 100644 --- a/test/backends/SessionTest.cpp +++ b/test/backends/SessionTest.cpp @@ -234,7 +234,4 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find all objects with many-to-m recipes.push_back(std::make_unique(7, "Apple Crumble")); recipes.push_back(std::make_unique(8, "Beans Chili")); recipes.push_back(std::make_unique(9, "Fruit Salad")); - - - } \ No newline at end of file diff --git a/test/backends/StatementCacheTest.cpp b/test/backends/StatementCacheTest.cpp index 436baaf..11bdc9c 100644 --- a/test/backends/StatementCacheTest.cpp +++ b/test/backends/StatementCacheTest.cpp @@ -15,13 +15,13 @@ class StatementCacheFixture { public: StatementCacheFixture() - : pool(test::connection::dns, 4), ses(pool) + : pool(test::connection::dns, 4)//, ses(pool) {} ~StatementCacheFixture() = default; protected: sql::connection_pool pool; - orm::session ses; + // orm::session ses; }; TEST_CASE_METHOD(StatementCacheFixture, "Acquire prepared statement", "[statement cache]") { diff --git a/test/orm/backend/test_statement.cpp b/test/orm/backend/test_statement.cpp index 6208ca3..d01d93a 100644 --- a/test/orm/backend/test_statement.cpp +++ b/test/orm/backend/test_statement.cpp @@ -14,8 +14,7 @@ utils::result test_statement::execute(const sql::parameter using namespace std::chrono_literals; std::mt19937 rng(query_.sql.size()); std::uniform_int_distribution dist(10, 50); - std::this_thread::sleep_for(std::chrono::milliseconds(50)); - // std::this_thread::sleep_for(std::chrono::milliseconds(dist(rng))); + std::this_thread::sleep_for(std::chrono::milliseconds(dist(rng))); return utils::ok(static_cast(8)); }