moved session to namespace query and move basic_schema::initialize to session constructor

This commit is contained in:
2026-04-23 16:01:44 +02:00
parent a34a3dc266
commit 3a82f4891a
30 changed files with 337 additions and 367 deletions
-36
View File
@@ -1,36 +0,0 @@
#ifndef ERROR_CODE_HPP
#define ERROR_CODE_HPP
#include <cstdint>
#include <system_error>
namespace matador::orm {
enum class error_code : uint8_t {
Ok = 0,
NoConnectionAvailable,
UnknownType,
NoPrimaryKey,
FailedToBuildQuery,
FailedToFindObject,
FailedToInsertObject,
Failed
};
class orm_category_impl final : public std::error_category
{
public:
[[nodiscard]] const char* name() const noexcept override;
[[nodiscard]] std::string message(int ev) const override;
};
const std::error_category& orm_category();
std::error_code make_error_code(error_code e);
std::error_condition make_error_condition(error_code e);
}
template <>
struct std::is_error_code_enum<matador::orm::error_code> : true_type {};
#endif //ERROR_CODE_HPP
+1 -2
View File
@@ -89,12 +89,11 @@ public:
[[nodiscard]] bool contains(const std::type_index &index) const;
void initialize(sql::executor &exec) const;
protected:
template<typename Type>
friend class schema_observer;
friend class producer_creator;
friend class producer_accessor;
object::repository repo_;
std::unordered_map<std::type_index, schema_node> schema_nodes_;
+12 -2
View File
@@ -6,7 +6,7 @@
namespace matador::query {
enum class error_code {
Success,
Ok,
Failure,
InvalidQuery,
InvalidSchema,
@@ -14,7 +14,17 @@ enum class error_code {
InvalidColumn,
InvalidConstraint,
InvalidDataType,
InvalidValue
InvalidValue,
InvalidRelationType,
MissingPrimaryKey,
UnknownType,
FailedToAcquireConnection,
FailedToFindPrimaryKey,
FailedToBuildQuery,
FailedToFindObject,
FailedToInsertObject,
FailedToAcquirePool,
UnexpectedError
};
class query_category_impl final : public std::error_category
+10 -10
View File
@@ -77,9 +77,9 @@ public:
, contexts_by_type_{contexts_by_type}
{}
utils::result<std::vector<insert_step>, query_build_error> build(const object::object_ptr<ObjectType> &ptr) {
utils::result<std::vector<insert_step>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
return utils::failure(query_build_error::UnknownType);
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
}
steps_.clear();
@@ -148,18 +148,18 @@ public:
const auto it = schema_.find(std::string{id});
if (it == schema_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
throw query_builder_exception(query_build_error::InvalidRelationType);
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
}
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
const auto rel_it = processing_many_to_many_relations_.insert(id);
@@ -205,18 +205,18 @@ public:
const auto it = schema_.find(std::string{id});
if (it == schema_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
throw query_builder_exception(query_build_error::InvalidRelationType);
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
}
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
const auto rel_it = processing_many_to_many_relations_.insert(id);
@@ -276,7 +276,7 @@ private:
const auto it = schema_.find(typeid(EntityType));
if (it == schema_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
// 1) Traverse relations first => dependencies will be inserted before this object
@@ -291,7 +291,7 @@ private:
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
throw query_builder_exception(query_build_error::UnknownType);
throw query_builder_exception(error_code::UnknownType, "Unknown type");
}
step.ctx = cit->second.insert;
step.bind_object = [ptr](sql::statement &stmt) { stmt.bind(*ptr); };
@@ -3,29 +3,19 @@
#include "matador/utils/error.hpp"
#include "matador/query/error_code.hpp"
#include <cstdint>
namespace matador::query {
enum class query_build_error : std::uint8_t {
Ok = 0,
UnknownType,
MissingPrimaryKey,
MissingObject,
UnexpectedError,
QueryError,
InvalidRelationType
};
class query_builder_exception final : public std::exception {
public:
explicit query_builder_exception(query_build_error error, utils::error &&err = {});
explicit query_builder_exception(error_code error, std::string &&msg);
[[nodiscard]] query_build_error error_type() const;
[[nodiscard]] const utils::error &error() const;
private:
const query_build_error error_type_;
utils::error error_;
};
+22 -26
View File
@@ -30,21 +30,22 @@ public:
, pk_name_(std::move(pk_name))
{}
std::shared_ptr<object::abstract_collection_resolver> produce(sql::executor &exec) override {
utils::result<sql::query_context, utils::error> build_query(const sql::dialect& d) override {
const auto *pk_column = table_[pk_name_];
const auto *join_column = table_[collection_name()];
const auto object_resolver = exec.resolver()->object_resolver<typename Type::value_type>();
auto stmt = query::select({*pk_column})
const auto stmt = select({*pk_column})
.from(table_)
.where(*join_column == utils::_)
.prepare(exec);
.compile(d);
if (!stmt) {
return {};
}
return utils::ok(stmt);
}
return std::make_shared<query_collection_resolver<Type>>(stmt.release(), root_type(), collection_name(), object_resolver);
std::shared_ptr<object::abstract_collection_resolver> produce(sql::statement&& stmt, const sql::resolver_service& rs) override {
const auto object_resolver = rs.object_resolver<typename Type::value_type>();
return std::make_shared<query_collection_resolver<Type>>(std::move(stmt), root_type(), collection_name(), object_resolver);
}
private:
@@ -76,10 +77,10 @@ public:
void on_has_many(const char * /*id*/, CollectionType &/*cont*/, const char *join_column, const utils::foreign_attributes &/*attr*/, std::enable_if_t<object::is_object_ptr<typename CollectionType::value_type>::value> * = nullptr) {
const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type));
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
if (!it->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"};
}
auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type>>(
@@ -101,7 +102,7 @@ public:
void on_has_many_to_many(const char *id, CollectionType &, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {
const auto it = schema_.find(id);
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
auto producer = std::make_unique<query_collection_resolver_producer<typename CollectionType::value_type>>(
@@ -118,7 +119,7 @@ public:
void on_has_many_to_many(const char *id, CollectionType &, const utils::foreign_attributes &/*attr*/) {
const auto it = schema_.find(id);
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
object::join_columns_collector collector;
@@ -151,28 +152,23 @@ public:
, table_(tab)
, pk_name_(std::move(pk_name)) {}
std::shared_ptr<object::abstract_type_resolver> produce(sql::executor &exec) override {
std::shared_ptr<object::abstract_type_resolver> produce(sql::statement&& stmt) override {
return std::make_shared<query_object_resolver<Type>>(std::move(stmt));
}
utils::result<sql::query_context, utils::error> build_query(const sql::dialect& d) override {
producer_creator pc(repo_, typeid(Type));
Type obj;
access::process(pc, obj);
select_query_builder qb(repo_);
const auto *pk_column = table_[pk_name_];
auto builder_result = qb.build<Type>(*pk_column == utils::_);
if (!builder_result) {
return nullptr;
const auto result = qb.build<Type>(*pk_column == utils::_);
if (!result) {
return utils::failure(result.err());
}
auto stmt = builder_result
.release()
.prepare(exec);
if (!stmt) {
return nullptr;
}
return std::make_shared<query_object_resolver<Type>>(stmt.release());
return utils::ok(result->compile(d));
}
private:
+16 -14
View File
@@ -4,6 +4,7 @@
#include "matador/query/query_builder_exception.hpp"
#include "matador/query/criteria.hpp"
#include "matador/query/error_code.hpp"
#include "matador/query/query.hpp"
#include "matador/query/criteria/criteria_visitor.hpp"
@@ -13,6 +14,7 @@
#include "matador/object/repository.hpp"
#include "matador/utils/primary_key_attribute.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include "matador/utils/value.hpp"
@@ -56,10 +58,10 @@ public:
: schema_(scm){}
template<class EntityType>
utils::result<fetchable_query, query_build_error> build(criteria_ptr clause = {}) {
utils::result<fetchable_query, utils::error> build(criteria_ptr clause = {}) {
const auto it = schema_.find(typeid(EntityType));
if (it == schema_.end()) {
return utils::failure(query_build_error::UnknownType);
return utils::failure{utils::error(error_code::UnknownType, "Entity type not found in schema")};
}
table_info_stack_.push({it->second.node().info(), it->second.table().as(build_alias('t', ++table_index))});
entity_query_data_ = { &table_info_stack_.top().table };
@@ -83,9 +85,9 @@ public:
return {utils::ok(std::move(q))};
} catch (const query_builder_exception &ex) {
return {utils::failure(ex.error_type())};
return {utils::failure(ex.error())};
} catch (...) {
return {utils::failure(query_build_error::UnexpectedError)};
return utils::failure{utils::error(error_code::UnexpectedError, "Unexpected error")};
}
}
@@ -132,7 +134,7 @@ public:
const auto it = schema_.find(typeid(typename CollectionType::value_type::value_type));
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
auto next = processed_tables_.find(it->second.name());
@@ -148,7 +150,7 @@ public:
table_info_stack_.pop();
if (!it->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"};
}
append_join(
@@ -168,7 +170,7 @@ public:
}
const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type));
if (result == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
auto next = processed_tables_.find(result->second.name());
@@ -181,7 +183,7 @@ public:
if (relation == processed_tables_.end()) {
const auto it = schema_.find(id);
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
relation = processed_tables_.emplace(id, it->second.table().as(build_alias('t', ++table_index))).first;
}
@@ -192,7 +194,7 @@ public:
table_info_stack_.pop();
if (!result->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"};
}
append_join(
@@ -212,7 +214,7 @@ public:
}
const auto result = schema_.find(typeid(typename ContainerType::value_type::value_type));
if (result == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
auto next = processed_tables_.find(result->second.name());
@@ -225,7 +227,7 @@ public:
if (relation == processed_tables_.end()) {
const auto it = schema_.find(id);
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
const auto t = it->second.table().as(build_alias('t', ++table_index));
relation = processed_tables_.insert({id, t}).first;
@@ -237,7 +239,7 @@ public:
table_info_stack_.pop();
if (!result->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"};
}
const auto join_columns = join_columns_collector_.collect<typename ContainerType::value_type::value_type>();
@@ -281,10 +283,10 @@ template<class Pointer>
void select_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) {
const auto it = schema_.find(typeid(typename Pointer::value_type));
if (it == schema_.end()) {
throw query_builder_exception{query_build_error::UnknownType};
throw query_builder_exception{error_code::UnknownType, "Unknown type"};
}
if (!it->second.node().info().has_primary_key()) {
throw query_builder_exception{query_build_error::MissingPrimaryKey};
throw query_builder_exception{error_code::MissingPrimaryKey, "Missing primary key"};
}
const auto& info = it->second.node().info();
@@ -1,8 +1,7 @@
#ifndef QUERY_SESSION_HPP
#define QUERY_SESSION_HPP
#include "matador/orm/error_code.hpp"
#include "matador/query/error_code.hpp"
#include "matador/query/select_query_builder.hpp"
#include "matador/query/criteria.hpp"
#include "matador/query/insert_query_builder.hpp"
@@ -24,7 +23,7 @@
#include <unordered_map>
#include <utility>
namespace matador::orm {
namespace matador::query {
utils::error make_error(error_code ec, const std::string &msg);
struct session_context {
@@ -41,7 +40,7 @@ struct session_context {
std::shared_ptr<sql::resolver_service> resolver_service = std::make_shared<sql::resolver_service>();
};
class session final : public sql::executor {
class session final /*: public sql::executor*/ {
public:
session(session_context &&ctx, const query::schema &scm);
@@ -65,12 +64,12 @@ public:
template<typename Type>
utils::result<sql::query_result<Type>, utils::error> find(query::criteria_ptr clause = {});
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) const override;
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::query_context &ctx) const override;
[[nodiscard]] utils::result<sql::statement, utils::error> 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;
[[nodiscard]] std::shared_ptr<sql::resolver_service> resolver() const override;
// [[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) const override;
// [[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::query_context &ctx) const override;
// [[nodiscard]] utils::result<sql::statement, utils::error> 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;
// [[nodiscard]] std::shared_ptr<sql::resolver_service> resolver() const override;
[[nodiscard]] const query::basic_schema &schema() const;
private:
@@ -105,7 +104,11 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
// Execute all steps; for Identity steps read RETURNING and write pk back into the object
for (query::insert_step &step : *steps) {
if (step.pk_generator == utils::generator_type::Sequence || step.pk_generator == utils::generator_type::Table) {
auto result = it->second.pk_generator().next_id(*this);
const auto conn = pool_.acquire();
if (!conn.valid()) {
return utils::failure(make_error(error_code::FailedToAcquirePool, "Failed to acquire connection pool for primary key generation."));
}
auto result = it->second.pk_generator().next_id(*conn);
if (!result.is_ok()) {
return utils::failure(result.err());
}
@@ -205,17 +208,17 @@ utils::result<object::object_ptr<Type>, utils::error> session::update(const obje
using namespace matador::utils;
using namespace matador::query;
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
auto res = query::update(it->second.name())
.set<Type>()
.where(col == _)
.prepare(*this);
if (!res) {
return utils::failure(res.err());
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
auto stmt = cache_.acquire(cit->second.update_one);
if (!stmt.is_ok()) {
return failure(stmt.err());
}
res->bind(*obj);
pk_object_binder binder(res.value(), res->bind_pos());
stmt->bind(*obj);
pk_object_binder binder(stmt.value(), stmt->bind_pos());
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
return utils::failure(update_result.err());
}
@@ -232,15 +235,16 @@ utils::result<void, utils::error> session::remove(const object::object_ptr<Type>
using namespace matador::query;
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
auto res = query::remove()
.from(it->second.name())
.where(col == _)
.prepare(*this);
if (!res) {
return utils::failure(res.err());
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
auto stmt = cache_.acquire(cit->second.delete_one);
if (!stmt.is_ok()) {
return failure(stmt.err());
}
pk_object_binder binder(res.value(), res->bind_pos());
pk_object_binder binder(*stmt, stmt->bind_pos());
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
return utils::failure(update_result.err());
}
@@ -255,24 +259,19 @@ utils::result<object::object_ptr<Type>, utils::error> session::find(const Primar
}
const auto &info = it->second.node().info();
if (!info.has_primary_key()) {
return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key."));
return utils::failure(make_error(error_code::FailedToFindPrimaryKey, "Type hasn't primary key."));
}
query::select_query_builder eqb(schema_);
const query::table_column c(&it->second.table(), info.primary_key_attribute()->name());
using namespace matador::query;
auto data = eqb.build<Type>(c == utils::_);
if (!data.is_ok()) {
return utils::failure(make_error(error_code::FailedToBuildQuery,
"Failed to build query for type " + info.name() + "."));
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
if (cit == contexts_by_type_.end()) {
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
}
auto stmt = cache_.acquire(cit->second.select_one);
if (!stmt.is_ok()) {
return utils::failure(stmt.err());
}
auto res = data->prepare(*this);
if (!res) {
return utils::failure(res.err());
}
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType &>(pk))
auto stmt_result = stmt->bind(0, const_cast<PrimaryKeyType &>(pk))
.template fetch_one<Type>();
if (!stmt_result) {
return utils::failure(make_error(error_code::FailedToFindObject,
@@ -296,12 +295,13 @@ utils::result<sql::query_result<Type>, utils::error> session::find(query::criter
"Failed to build query for type " + it->second.name() + "."));
}
auto result = data->prepare(*this);
if (!result.is_ok()) {
return utils::failure(result.err());
auto ctx = data->compile(dialect_);
auto stmt = cache_.acquire(ctx);
if (!stmt.is_ok()) {
return utils::failure(stmt.err());
}
return result->template fetch<Type>();
return stmt->template fetch<Type>();
}
}
#endif //QUERY_SESSION_HPP
@@ -3,14 +3,22 @@
#include "matador/object/abstract_collection_resolver.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include <memory>
namespace matador::sql {
class executor;
class dialect;
class statement;
class resolver_service;
class collection_resolver_producer {
public:
virtual ~collection_resolver_producer() = default;
virtual std::shared_ptr<object::abstract_collection_resolver> produce(executor &exec) = 0;
virtual utils::result<query_context, utils::error> build_query(const dialect& d) = 0;
virtual std::shared_ptr<object::abstract_collection_resolver> produce(statement&& stmt, const resolver_service& rs) = 0;
[[nodiscard]] const std::type_index& root_type() const;
[[nodiscard]] const std::type_index& type() const;
@@ -3,14 +3,21 @@
#include "matador/object/abstract_type_resolver.hpp"
#include "matador/sql/query_context.hpp"
#include "matador/utils/error.hpp"
#include "matador/utils/result.hpp"
#include <memory>
namespace matador::sql {
class executor;
class dialect;
class statement;
class object_resolver_producer {
public:
virtual ~object_resolver_producer() = default;
virtual std::shared_ptr<object::abstract_type_resolver> produce(executor &exec) = 0;
virtual utils::result<query_context, utils::error> build_query(const dialect& d) = 0;
virtual std::shared_ptr<object::abstract_type_resolver> produce(statement&& stmt) = 0;
[[nodiscard]] const std::type_index& type() const;
-10
View File
@@ -29,12 +29,6 @@ enum class sql_command {
AlterSchema
};
enum class return_mode {
None,
GeneratedKeys,
Rows
};
struct query_context {
std::string sql;
size_t sql_hash{};
@@ -48,14 +42,10 @@ struct query_context {
// Data for resolving query result
std::shared_ptr<resolver_service> resolver{};
std::type_index result_type = typeid(void);
return_mode mode = return_mode::None;
[[nodiscard]] bool is_ddl() const { return command == sql_command::Create || command == sql_command::Alter; }
[[nodiscard]] bool is_dml() const { return command == sql_command::Insert || command == sql_command::Update || command == sql_command::Delete; }
[[nodiscard]] bool is_query() const { return command == sql_command::Select; }
[[nodiscard]] bool expects_rows() const { return mode == return_mode::Rows; }
[[nodiscard]] bool expects_generated_keys() const { return mode == return_mode::GeneratedKeys; }
};
}