From 75d5cba6c37d3bb8438400b39c77cbbe99d0cbb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20K=C3=BChl?= Date: Sun, 26 Apr 2026 15:50:11 +0200 Subject: [PATCH] session progress --- backends/postgres/src/postgres_connection.cpp | 22 +++--- backends/postgres/src/postgres_statement.cpp | 8 +- include/matador/object/object_generator.hpp | 6 +- include/matador/query/basic_schema.hpp | 4 +- include/matador/query/error_code.hpp | 1 + .../matador/query/insert_query_builder.hpp | 35 +++++---- include/matador/query/session.hpp | 41 ++++------- include/matador/sql/error_code.hpp | 34 ++++----- .../matador/sql/object_parameter_binder.hpp | 3 + include/matador/sql/query_context.hpp | 2 +- include/matador/sql/statement.hpp | 2 +- source/orm/query/basic_schema.cpp | 10 ++- source/orm/query/criteria_evaluator.cpp | 4 +- source/orm/query/error_code.cpp | 2 + source/orm/query/manual_pk_generator.cpp | 4 +- source/orm/query/query_builder.cpp | 2 +- source/orm/query/sequence_pk_generator.cpp | 4 +- source/orm/query/session.cpp | 73 ++++--------------- source/orm/query/table_pk_generator.cpp | 4 +- source/orm/sql/error_code.cpp | 32 ++++---- source/orm/sql/statement_cache.cpp | 14 ++-- test/backends/QueryTest.cpp | 47 ++++++++++-- test/backends/SessionInsertHasMany.cpp | 2 +- test/backends/SessionTest.cpp | 4 +- test/orm/query/InsertQueryBuilderTest.cpp | 6 +- 25 files changed, 191 insertions(+), 175 deletions(-) diff --git a/backends/postgres/src/postgres_connection.cpp b/backends/postgres/src/postgres_connection.cpp index e8f5367..713ec01 100644 --- a/backends/postgres/src/postgres_connection.cpp +++ b/backends/postgres/src/postgres_connection.cpp @@ -34,7 +34,7 @@ utils::result postgres_connection::open() { const std::string msg = PQerrorMessage(conn_); PQfinish(conn_); conn_ = nullptr; - return utils::failure(make_error(sql::error_code::OPEN_ERROR, nullptr, conn_, "Failed to connect")); + return utils::failure(make_error(sql::error_code::OpenError, nullptr, conn_, "Failed to connect")); } return utils::ok(); @@ -70,7 +70,7 @@ utils::result postgres_connection::server_version( const auto server_version = PQserverVersion(conn_); if (server_version == 0) { - return utils::failure(make_error(sql::error_code::FAILURE, nullptr, conn_, "Failed to get server version")); + return utils::failure(make_error(sql::error_code::Failure, nullptr, conn_, "Failed to get server version")); } return utils::ok(utils::version{ @@ -86,7 +86,7 @@ utils::result, utils::error> postgres_co PGresult *res = PQexec(conn_, context.sql.c_str()); if (is_result_error(res)) { - const auto err = make_error(sql::error_code::FETCH_FAILED, res, conn_, "Failed to fetch", context.sql); + const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Failed to fetch", context.sql); PQclear(res); return utils::failure(err); } @@ -95,7 +95,7 @@ utils::result, utils::error> postgres_co const int num_col = PQnfields(res); if (prototype.size() != static_cast(num_col)) { - const auto err = make_error(sql::error_code::FETCH_FAILED, res, conn_, "Number of received columns doesn't match expected columns.", context.sql); + const auto err = make_error(sql::error_code::FetchFailed, res, conn_, "Number of received columns doesn't match expected columns.", context.sql); PQclear(res); return utils::failure(err); } @@ -133,7 +133,7 @@ utils::result postgres_connection::execute(co PGresult *res = PQexec(conn_, stmt.c_str()); if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", stmt)); + return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", stmt)); } const size_t affected_rows = utils::to(PQcmdTuples(res)); @@ -149,7 +149,7 @@ utils::result, utils::error> postgres_conne PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast(context.bind_vars.size()), nullptr); if (is_result_error(result)) { - return utils::failure(make_error(sql::error_code::PREPARE_FAILED, result, conn_, "Failed to prepare", context.sql)); + return utils::failure(make_error(sql::error_code::PrepareFailed, result, conn_, "Failed to prepare", context.sql)); } std::unique_ptr s(std::make_unique(conn_, result, statement_name, context)); @@ -161,7 +161,7 @@ utils::result postgres_connection::execute(co PGresult *res = PQexec(conn_, context.sql.c_str()); if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { - return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to execute", context.sql)); + return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to execute", context.sql)); } const size_t affected_rows = utils::to(PQcmdTuples(res)); @@ -256,7 +256,7 @@ utils::result, utils::error> postgres_connection: PGresult *res = PQexec(conn_, stmt.c_str()); if (is_result_error(res)) { - return utils::failure(make_error(sql::error_code::DESCRIBE_FAILED, res, conn_, "Failed to describe", stmt)); + return utils::failure(make_error(sql::error_code::DescribeFailed, res, conn_, "Failed to describe", stmt)); } postgres_result_reader reader(res); @@ -297,13 +297,13 @@ utils::result postgres_connection::exists(const std::string PGresult *res = PQexec(conn_, stmt.c_str()); if (is_result_error(res)) { - return utils::failure(make_error(sql::error_code::TABLE_EXISTS_FAILED, res, conn_, "Failed check if table exists", stmt)); + return utils::failure(make_error(sql::error_code::TableExistsFailed, res, conn_, "Failed check if table exists", stmt)); } const auto result = utils::to(PQcmdTuples(res)); if (!result) { PQclear(res); - return utils::failure(make_error(sql::error_code::FAILURE, res, conn_, "Failed to convert result value", stmt)); + return utils::failure(make_error(sql::error_code::Failure, res, conn_, "Failed to convert result value", stmt)); } PQclear(res); @@ -315,7 +315,7 @@ utils::result postgres_connection::sequence_exists(const std PGresult* res = PQexec(conn_, sql.c_str()); if (PQresultStatus(res) != PGRES_TUPLES_OK) { - return utils::failure(make_error(sql::error_code::SEQUENCE_EXISTS_FAILED, res, conn_, "Failed check if sequence exists", sql)); + return utils::failure(make_error(sql::error_code::SequenceExistsFailed, res, conn_, "Failed check if sequence exists", sql)); } const bool exists = PQgetisnull(res, 0, 0) ? false : true; diff --git a/backends/postgres/src/postgres_statement.cpp b/backends/postgres/src/postgres_statement.cpp index 216f510..0cbb299 100644 --- a/backends/postgres/src/postgres_statement.cpp +++ b/backends/postgres/src/postgres_statement.cpp @@ -21,7 +21,7 @@ postgres_statement::~postgres_statement() { utils::result postgres_statement::execute(const sql::parameter_binder& bindings) { const auto* postgres_bindings = dynamic_cast(&bindings); if (!postgres_bindings) { - return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings")); + return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings")); } if (query_.command == sql::sql_command::Insert) { @@ -37,7 +37,7 @@ utils::result postgres_statement::execute(con 0); if (is_result_error(res)) { - return utils::failure(make_error(sql::error_code::EXECUTE_FAILED, res, db_, "Failed to execute statement", query_.sql)); + return utils::failure(make_error(sql::error_code::ExecuteFailed, res, db_, "Failed to execute statement", query_.sql)); } size_t value{0}; @@ -53,7 +53,7 @@ utils::result postgres_statement::execute(con utils::result, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) { const auto* postgres_bindings = dynamic_cast(&bindings); if (!postgres_bindings) { - return utils::failure(utils::error(sql::error_code::EXECUTE_FAILED, "Failed to cast bindings to postgres bindings")); + return utils::failure(utils::error(sql::error_code::ExecuteFailed, "Failed to cast bindings to postgres bindings")); } PGresult *res = PQexecPrepared(db_, name_.c_str(), @@ -64,7 +64,7 @@ utils::result, utils::error> postgres_st 0); if (is_result_error(res)) { - return utils::failure(make_error(sql::error_code::FETCH_FAILED, res, db_, "Failed to fetch statement", query_.sql)); + return utils::failure(make_error(sql::error_code::FetchFailed, res, db_, "Failed to fetch statement", query_.sql)); } return utils::ok(std::make_unique(std::make_unique(res), diff --git a/include/matador/object/object_generator.hpp b/include/matador/object/object_generator.hpp index d97f11c..eba305e 100644 --- a/include/matador/object/object_generator.hpp +++ b/include/matador/object/object_generator.hpp @@ -135,7 +135,11 @@ private: template void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) { - auto &ref = emplace_attribute(id, { attr.size(), utils::constraints::PrimaryKey }, null_option_type::NotNull); + utils::constraints cs = utils::constraints::PrimaryKey; + if (attr.generator() == utils::generator_type::Identity) { + cs |= utils::constraints::Identity; + } + auto &ref = emplace_attribute(id, { attr.size(), cs }, null_option_type::NotNull); prepare_primary_key(ref, utils::identifier(x)); } diff --git a/include/matador/query/basic_schema.hpp b/include/matador/query/basic_schema.hpp index bdad5d8..7360e28 100644 --- a/include/matador/query/basic_schema.hpp +++ b/include/matador/query/basic_schema.hpp @@ -89,11 +89,13 @@ public: [[nodiscard]] bool contains(const std::type_index &index) const; + const std::unordered_map>& resolver_producers() const; + const std::unordered_map, object::collection_composite_key_hash>& collection_resolver_producers() const; + protected: template friend class schema_observer; friend class producer_creator; - friend class producer_accessor; object::repository repo_; std::unordered_map schema_nodes_; diff --git a/include/matador/query/error_code.hpp b/include/matador/query/error_code.hpp index db010ad..6a9fe70 100644 --- a/include/matador/query/error_code.hpp +++ b/include/matador/query/error_code.hpp @@ -14,6 +14,7 @@ enum class error_code { InvalidColumn, InvalidConstraint, InvalidDataType, + InvalidObject, InvalidValue, InvalidRelationType, MissingPrimaryKey, diff --git a/include/matador/query/insert_query_builder.hpp b/include/matador/query/insert_query_builder.hpp index 79e9a66..92d0082 100644 --- a/include/matador/query/insert_query_builder.hpp +++ b/include/matador/query/insert_query_builder.hpp @@ -86,8 +86,11 @@ public: visited_.clear(); ptr_ = ptr; - build_for(ptr, steps_); + const auto result = build_for(ptr, steps_); ptr_.reset(); + if (!result) { + return utils::failure(result.err()); + } // relation inserts must run after all entity inserts were collected for (auto &s : relation_steps_) { @@ -151,8 +154,7 @@ public: throw query_builder_exception(error_code::UnknownType, "Unknown type"); } - using relation_value_type = object::many_to_many_relation; - + using relation_value_type = object::many_to_many_relation; if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) { throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type"); } @@ -162,7 +164,7 @@ public: throw query_builder_exception(error_code::UnknownType, "Unknown type"); } - const auto rel_it = processing_many_to_many_relations_.insert(id); + std::ignore = processing_many_to_many_relations_.insert(id); std::vector rel_steps; for (auto &obj : objects) { if (!obj) { @@ -174,7 +176,7 @@ public: build_for(obj, relation_steps_); } - auto rel = object::make_object(join_column, inverse_join_column, obj, ptr_); + auto rel = object::make_object(join_column, inverse_join_column, ptr_, obj); access::process(*this, *rel); @@ -219,7 +221,7 @@ public: throw query_builder_exception(error_code::UnknownType, "Unknown type"); } - const auto rel_it = processing_many_to_many_relations_.insert(id); + std::ignore = processing_many_to_many_relations_.insert(id); std::vector rel_steps; for (auto &obj : objects) { if (!obj) { @@ -263,24 +265,28 @@ private: }; template - void build_for(const object::object_ptr &ptr, std::vector &steps) { + utils::result build_for(const object::object_ptr &ptr, std::vector &steps) { if (!ptr) { - return; + return utils::failure(utils::error{error_code::InvalidObject, "Object is null"}); } const auto key = make_visit_key(ptr); if (visited_.find(key) != visited_.end()) { - return; + return utils::ok(); } visited_.insert(key); const auto it = schema_.find(typeid(EntityType)); if (it == schema_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); + return utils::failure(utils::error{error_code::UnknownType, "Unknown type"}); } // 1) Traverse relations first => dependencies will be inserted before this object - access::process(*this, *ptr); + try { + access::process(*this, *ptr); + } catch (const query_builder_exception &ex) { + return utils::failure(ex.error()); + } // 2) Build INSERT for this object const auto &info = it->second.node().info(); @@ -291,27 +297,28 @@ private: const auto cit = contexts_by_type_.find(it->second.node().info().type_index()); if (cit == contexts_by_type_.end()) { - throw query_builder_exception(error_code::UnknownType, "Unknown type"); + return utils::failure(utils::error{error_code::UnknownType, "Unknown type"}); } step.ctx = cit->second.insert; step.bind_object = [ptr](sql::statement &stmt) { stmt.bind(*ptr); }; if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) { const auto pk_name = info.primary_key_attribute()->name(); const table_column pk_col(&it->second.table(), pk_name); - // step.query = fetchable_query{insert().into(it->second.table()).values(*ptr).returning(pk_col)}; step.apply_returning = [ptr, &step, pk_name = pk_name](const sql::record &rec) { const auto& f = rec.at(pk_name); utils::identifier id; id.assign(f.value()); step.pk_accessor.set(*ptr, id); }; - } else if (info.has_primary_key() && step.pk_generator == utils::generator_type::Sequence || step.pk_generator == utils::generator_type::Table) { + } else if (info.has_primary_key() && (step.pk_generator == utils::generator_type::Sequence || step.pk_generator == utils::generator_type::Table)) { step.apply_primary_key = [ptr, &step](const utils::identifier &id) { step.pk_accessor.set(*ptr, id); }; } step.make_object_persistent = [ptr] { ptr.change_state(object::object_state::Persistent); }; steps.push_back(std::move(step)); + + return utils::ok(); } template diff --git a/include/matador/query/session.hpp b/include/matador/query/session.hpp index caf56a5..6d34af8 100644 --- a/include/matador/query/session.hpp +++ b/include/matador/query/session.hpp @@ -8,12 +8,10 @@ #include "matador/query/query.hpp" #include "matador/query/query_contexts.hpp" #include "matador/query/generator.hpp" -#include "matador/query/schema.hpp" #include "matador/sql/connection.hpp" #include "matador/sql/connection_pool.hpp" #include "matador/sql/executor.hpp" -#include "matador/sql/record.hpp" #include "matador/sql/resolver_service.hpp" #include "matador/sql/statement.hpp" #include "matador/sql/statement_cache.hpp" @@ -42,7 +40,7 @@ struct session_context { class session final /*: public sql::executor*/ { public: - session(session_context &&ctx, const query::schema &scm); + session(session_context &&ctx, const basic_schema &scm); /** * Insert the given object into the session. @@ -64,20 +62,14 @@ public: template utils::result, utils::error> find(query::criteria_ptr clause = {}); - // [[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; - // [[nodiscard]] std::shared_ptr resolver() const override; - [[nodiscard]] const query::basic_schema &schema() const; + [[nodiscard]] const basic_schema &schema() const; private: sql::connection_pool pool_; mutable sql::statement_cache cache_; const sql::dialect &dialect_; - const query::basic_schema &schema_; + const basic_schema &schema_; mutable std::unordered_map > prototypes_; std::shared_ptr resolver_service_; std::unordered_map contexts_by_type_; @@ -257,28 +249,26 @@ utils::result, utils::error> session::find(const Primar if (it == schema_.end()) { return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type.")); } - const auto &info = it->second.node().info(); - if (!info.has_primary_key()) { + + if (const auto &info = it->second.node().info(); !info.has_primary_key()) { return utils::failure(make_error(error_code::FailedToFindPrimaryKey, "Type hasn't primary key.")); } - 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.")); + select_query_builder eqb(schema_); + auto data = eqb.build(*it->second.table().primary_key_column() == pk); + if (!data.is_ok()) { + return utils::failure(make_error(error_code::FailedToBuildQuery, + "Failed to build query for type " + it->second.name() + ".")); } - auto stmt = cache_.acquire(cit->second.select_one); + + auto ctx = data->compile(dialect_); + ctx.resolver = resolver_service_; + auto stmt = cache_.acquire(ctx); if (!stmt.is_ok()) { return utils::failure(stmt.err()); } - auto stmt_result = stmt->bind(0, const_cast(pk)) - .template fetch_one(); - if (!stmt_result) { - return utils::failure(make_error(error_code::FailedToFindObject, - "Failed to find object of type " + info.name() + " with primary key " + - std::to_string(pk) + ".")); - } - return utils::ok(*stmt_result); + return stmt->template fetch_one(); } template @@ -296,6 +286,7 @@ utils::result, utils::error> session::find(query::criter } auto ctx = data->compile(dialect_); + ctx.resolver = resolver_service_; auto stmt = cache_.acquire(ctx); if (!stmt.is_ok()) { return utils::failure(stmt.err()); diff --git a/include/matador/sql/error_code.hpp b/include/matador/sql/error_code.hpp index 6705e31..7f3d58c 100644 --- a/include/matador/sql/error_code.hpp +++ b/include/matador/sql/error_code.hpp @@ -7,23 +7,23 @@ namespace matador::sql { enum class error_code : uint8_t { - OK = 0, - INVALID_QUERY, - UNKNOWN_TABLE, - UNKNOWN_COLUMN, - BIND_FAILED, - EXECUTE_FAILED, - FETCH_FAILED, - PREPARE_FAILED, - DESCRIBE_FAILED, - TABLE_EXISTS_FAILED, - RETRIEVE_DATA_FAILED, - SEQUENCE_EXISTS_FAILED, - RESET_FAILED, - OPEN_ERROR, - CLOSE_ERROR, - STATEMENT_LOCKED, - FAILURE + Ok = 0, + InvalidQuery, + UnknownTable, + UnknownColumn, + BindFailed, + ExecuteFailed, + FetchFailed, + PrepareFailed, + DescribeFailed, + TableExistsFailed, + RetrieveDataFailed, + SequenceExistsFailed, + ResetFailed, + OpenError, + CloseError, + StatementLocked, + Failure }; class sql_category_impl final : public std::error_category diff --git a/include/matador/sql/object_parameter_binder.hpp b/include/matador/sql/object_parameter_binder.hpp index 155cdf0..dddf53c 100644 --- a/include/matador/sql/object_parameter_binder.hpp +++ b/include/matador/sql/object_parameter_binder.hpp @@ -21,6 +21,9 @@ public: static void on_base(const BaseType&) {} template < class Type > void on_primary_key(const char * /*id*/, Type &val, const utils::primary_key_attribute& attr) { + if (attr.generator() == utils::generator_type::Identity) { + return; + } utils::data_type_traits::bind_value(*binder_, index_++, val, attr.size()); } void on_revision(const char *id, uint64_t &/*rev*/); diff --git a/include/matador/sql/query_context.hpp b/include/matador/sql/query_context.hpp index bb6bfd4..54e19da 100644 --- a/include/matador/sql/query_context.hpp +++ b/include/matador/sql/query_context.hpp @@ -38,7 +38,7 @@ struct query_context { std::string table_name{}; std::vector prototype{}; std::vector bind_vars{}; - std::vector bind_types{}; + // std::vector bind_types{}; // Data for resolving query result std::shared_ptr resolver{}; std::type_index result_type = typeid(void); diff --git a/include/matador/sql/statement.hpp b/include/matador/sql/statement.hpp index 96afae1..48d555a 100644 --- a/include/matador/sql/statement.hpp +++ b/include/matador/sql/statement.hpp @@ -196,7 +196,7 @@ utils::result, utils::error> statement::fetch_one() { }); auto first = records.begin(); if (first == records.end()) { - return utils::failure(utils::error{error_code::FETCH_FAILED,"Failed to find entity."}); + return utils::failure(utils::error{error_code::FetchFailed,"Failed to find entity."}); } return utils::ok(first.optr()); diff --git a/source/orm/query/basic_schema.cpp b/source/orm/query/basic_schema.cpp index 8be94a2..1cb114a 100644 --- a/source/orm/query/basic_schema.cpp +++ b/source/orm/query/basic_schema.cpp @@ -85,4 +85,12 @@ basic_schema::const_iterator basic_schema::find(const std::string &name) const { bool basic_schema::contains(const std::type_index &index) const { return schema_nodes_.count(index) == 1; } -} \ No newline at end of file + +const std::unordered_map> & basic_schema::resolver_producers() const { + return resolver_producers_; +} + +const std::unordered_map, object::collection_composite_key_hash> & basic_schema::collection_resolver_producers() const { + return collection_resolver_producers_; +} +} diff --git a/source/orm/query/criteria_evaluator.cpp b/source/orm/query/criteria_evaluator.cpp index d880efd..1e626c6 100644 --- a/source/orm/query/criteria_evaluator.cpp +++ b/source/orm/query/criteria_evaluator.cpp @@ -52,7 +52,9 @@ template struct overload : Ts... { using Ts::operator()...; }; template overload(Ts...) -> overload; void criteria_evaluator::visit(const binary_criteria &node) { - query_.bind_vars.emplace_back(node.col().name()); + if (std::holds_alternative(node.value())) { + query_.bind_vars.emplace_back(node.col().name()); + } clause_ += prepare_criteria(dialect_, node.col()) + " " + detail::BinaryOperatorEnum.to_string(node.operand()) + " "; evaluate_value(node.value()); diff --git a/source/orm/query/error_code.cpp b/source/orm/query/error_code.cpp index 5a6cb43..6c74d4a 100644 --- a/source/orm/query/error_code.cpp +++ b/source/orm/query/error_code.cpp @@ -23,6 +23,8 @@ std::string query_category_impl::message(int ev) const { return "Invalid constraint"; case error_code::InvalidDataType: return "Invalid data type"; + case error_code::InvalidObject: + return "Invalid object"; case error_code::InvalidValue: return "Invalid value"; case error_code::InvalidRelationType: diff --git a/source/orm/query/manual_pk_generator.cpp b/source/orm/query/manual_pk_generator.cpp index 6c3463c..73a82a7 100644 --- a/source/orm/query/manual_pk_generator.cpp +++ b/source/orm/query/manual_pk_generator.cpp @@ -8,10 +8,10 @@ manual_pk_generator::manual_pk_generator() } utils::result manual_pk_generator::next_id(const sql::executor &/*exec*/) { - return utils::failure(utils::error(sql::error_code::FAILURE, "Manual PK generator not implemented")); + return utils::failure(utils::error(sql::error_code::Failure, "Manual PK generator not implemented")); } utils::result manual_pk_generator::current_id(const sql::executor& /*exec*/) { - return utils::failure(utils::error(sql::error_code::FAILURE, "Manual PK generator not implemented")); + return utils::failure(utils::error(sql::error_code::Failure, "Manual PK generator not implemented")); } } diff --git a/source/orm/query/query_builder.cpp b/source/orm/query/query_builder.cpp index e049d11..c89dcb8 100644 --- a/source/orm/query/query_builder.cpp +++ b/source/orm/query/query_builder.cpp @@ -357,7 +357,7 @@ void query_builder::visit(internal::query_set_part& part) { if (!first) { query_.sql.append(", "); } - query_.sql.append(dialect_->prepare_identifier_string(column_value.col().name()) + "="); + query_.sql.append(dialect_->prepare_identifier_string(column_value.col().column_name()) + "="); query_.sql.append(determine_value(*dialect_, query_, column_value.expression())); first = false; } diff --git a/source/orm/query/sequence_pk_generator.cpp b/source/orm/query/sequence_pk_generator.cpp index 66261eb..3f9f242 100644 --- a/source/orm/query/sequence_pk_generator.cpp +++ b/source/orm/query/sequence_pk_generator.cpp @@ -13,7 +13,7 @@ sequence_pk_generator::sequence_pk_generator(const std::string& sequence_name) utils::result sequence_pk_generator::next_id(const sql::executor& exec) { return next_id_query_.fetch_value(exec).and_then([](const std::optional id) -> utils::result { if (!id) { - return utils::failure(utils::error(sql::error_code::RETRIEVE_DATA_FAILED, "Sequence returned no value")); + return utils::failure(utils::error(sql::error_code::RetrieveDataFailed, "Sequence returned no value")); } return utils::ok(*id); }); @@ -22,7 +22,7 @@ utils::result sequence_pk_generator::next_id(const sql::e utils::result sequence_pk_generator::current_id(const sql::executor& exec) { return current_id_query_.fetch_value(exec).and_then([](const std::optional id) -> utils::result { if (!id) { - return utils::failure(utils::error(sql::error_code::RETRIEVE_DATA_FAILED, "Sequence returned no value")); + return utils::failure(utils::error(sql::error_code::RetrieveDataFailed, "Sequence returned no value")); } return utils::ok(*id); }); diff --git a/source/orm/query/session.cpp b/source/orm/query/session.cpp index c6af8f7..a8ba604 100644 --- a/source/orm/query/session.cpp +++ b/source/orm/query/session.cpp @@ -10,21 +10,11 @@ #include namespace matador::query { -class producer_accessor { -public: - static const std::unordered_map>& resolver_producers(const basic_schema& scm) { - return scm.resolver_producers_; - } - static const std::unordered_map, object::collection_composite_key_hash>& collection_resolver_producers(const basic_schema& scm) { - return scm.collection_resolver_producers_; - } -}; - utils::error make_error(const error_code ec, const std::string &msg) { return utils::error(ec, msg); } -session::session(session_context&& ctx, const query::schema &scm) +session::session(session_context&& ctx, const basic_schema &scm) : pool_(ctx.dns, ctx.connection_count, [ctx](const sql::connection_info& info) { return sql::connection(info, ctx.resolver_service); }) , cache_(ctx.bus, pool_, ctx.cache_size) , dialect_(sql::backend_provider::instance().connection_dialect(pool_.info().type)) @@ -32,15 +22,15 @@ session::session(session_context&& ctx, const query::schema &scm) , resolver_service_(ctx.resolver_service) { using namespace matador::utils; for (const auto &[type, node] : schema_) { - query::query_contexts queries; + query_contexts queries; // SELECT all - queries.select_all = query::select(node.table()) + queries.select_all = select(node.table()) .from(node.name()) .compile(dialect_); if (node.table().has_primary_key()) { // SELECT one - queries.select_one = query::select(node.table()) + queries.select_one = select(node.table()) .from(node.name()) .where(*node.table().primary_key_column() == _) .compile(dialect_); @@ -58,9 +48,16 @@ session::session(session_context&& ctx, const query::schema &scm) .compile(dialect_); } // INSERT one + std::vector columns; + for (const auto &col: node.table().columns()) { + if (col.is_primary_key() && utils::is_constraint_set(col.attributes().options(), constraints::Identity)) { + continue; + } + columns.push_back(col); + } queries.insert = query::insert() - .into(node.name(), node.table()) - .values(query::generator::placeholders(node.table().columns().size())) + .into(node.name(), columns) + .values(generator::placeholders(columns.size())) .compile(dialect_); queries.insert.resolver = resolver_service_; @@ -71,8 +68,7 @@ session::session(session_context&& ctx, const query::schema &scm) contexts_by_type_[node.node().type_index()] = queries; } - auto factory = std::make_shared(); - for (const auto &pair : producer_accessor::resolver_producers(schema_)) { + for (const auto &pair : schema_.resolver_producers()) { auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result { query_ctx.resolver = resolver_service_; return cache_.acquire(query_ctx); @@ -89,8 +85,7 @@ session::session(session_context&& ctx, const query::schema &scm) } } - auto collection_factory = std::make_shared(); - for (const auto &pair : producer_accessor::collection_resolver_producers(schema_)) { + for (const auto &pair : schema_.collection_resolver_producers()) { auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result { query_ctx.resolver = resolver_service_; return cache_.acquire(query_ctx); @@ -108,43 +103,7 @@ session::session(session_context&& ctx, const query::schema &scm) } } -// const class sql::dialect &session::dialect() const { -// return dialect_; -// } -// -// std::shared_ptr session::resolver() const { -// return resolver_service_; -// } - -const query::basic_schema & session::schema() const { +const basic_schema & session::schema() const { return schema_; } - -// 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; -// } } diff --git a/source/orm/query/table_pk_generator.cpp b/source/orm/query/table_pk_generator.cpp index b091510..11f96a9 100644 --- a/source/orm/query/table_pk_generator.cpp +++ b/source/orm/query/table_pk_generator.cpp @@ -21,7 +21,7 @@ table_pk_generator::table_pk_generator(const std::string& table_name, const std: utils::result table_pk_generator::next_id(const sql::executor &exec) { return next_id_query_.fetch_value(exec).and_then([](const std::optional id) -> utils::result { if (!id) { - return utils::failure(utils::error(sql::error_code::RETRIEVE_DATA_FAILED, "Sequence returned no value")); + return utils::failure(utils::error(sql::error_code::RetrieveDataFailed, "Sequence returned no value")); } return utils::ok(*id); }); @@ -30,7 +30,7 @@ utils::result table_pk_generator::next_id(const sql::exec utils::result table_pk_generator::current_id(const sql::executor& exec) { return current_id_query_.fetch_value(exec).and_then([](const std::optional id) -> utils::result { if (!id) { - return utils::failure(utils::error(sql::error_code::RETRIEVE_DATA_FAILED, "Sequence returned no value")); + return utils::failure(utils::error(sql::error_code::RetrieveDataFailed, "Sequence returned no value")); } return utils::ok(*id); }); diff --git a/source/orm/sql/error_code.cpp b/source/orm/sql/error_code.cpp index 8b4d96b..610f4c1 100644 --- a/source/orm/sql/error_code.cpp +++ b/source/orm/sql/error_code.cpp @@ -8,37 +8,37 @@ const char * sql_category_impl::name() const noexcept { std::string sql_category_impl::message(const int ev) const { switch (static_cast(ev)) { - case error_code::OK: + case error_code::Ok: return "OK"; - case error_code::INVALID_QUERY: + case error_code::InvalidQuery: return "Invalid query"; - case error_code::UNKNOWN_TABLE: + case error_code::UnknownTable: return "Unknown table"; - case error_code::UNKNOWN_COLUMN: + case error_code::UnknownColumn: return "Unknown column"; - case error_code::BIND_FAILED: + case error_code::BindFailed: return "Bind failed"; - case error_code::EXECUTE_FAILED: + case error_code::ExecuteFailed: return "Execute failed"; - case error_code::FETCH_FAILED: + case error_code::FetchFailed: return "Fetch failed"; - case error_code::PREPARE_FAILED: + case error_code::PrepareFailed: return "Prepare failed"; - case error_code::DESCRIBE_FAILED: + case error_code::DescribeFailed: return "Describe failed"; - case error_code::TABLE_EXISTS_FAILED: + case error_code::TableExistsFailed: return "Table exists failed"; - case error_code::RETRIEVE_DATA_FAILED: + case error_code::RetrieveDataFailed: return "Retrieve data failed"; - case error_code::SEQUENCE_EXISTS_FAILED: + case error_code::SequenceExistsFailed: return "Sequence exists failed"; - case error_code::RESET_FAILED: + case error_code::ResetFailed: return "Reset failed"; - case error_code::OPEN_ERROR: + case error_code::OpenError: return "Open failed"; - case error_code::CLOSE_ERROR: + case error_code::CloseError: return "Close failed"; - case error_code::FAILURE: + case error_code::Failure: return "Failure"; default: return "Unknown error"; diff --git a/source/orm/sql/statement_cache.cpp b/source/orm/sql/statement_cache.cpp index 2584fec..ed64f54 100644 --- a/source/orm/sql/statement_cache.cpp +++ b/source/orm/sql/statement_cache.cpp @@ -41,7 +41,7 @@ public: const auto now = std::chrono::steady_clock::now(); bus_.publish(statement_lock_failed_event{query, now, now - metrics.lock_attempt_start}); return utils::failure(utils::error{ - error_code::STATEMENT_LOCKED, + error_code::StatementLocked, "Failed to execute statement because it is already in use" }); } @@ -51,7 +51,7 @@ public: auto guard = statement_guard(*this); if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) { return utils::failure(utils::error{ - error_code::EXECUTE_FAILED, + error_code::ExecuteFailed, "Failed to execute statement because couldn't lock connection" }); } @@ -75,14 +75,14 @@ public: if (!try_lock()) { ++metrics.lock_attempts; return utils::failure(utils::error{ - error_code::STATEMENT_LOCKED, + error_code::StatementLocked, "Failed to execute statement because it is already in use" }); } auto guard = statement_guard(*this); if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) { return utils::failure(utils::error{ - error_code::EXECUTE_FAILED, + error_code::ExecuteFailed, "Failed to execute statement because couldn't lock connection" }); } @@ -104,7 +104,7 @@ protected: for (size_t attempt = 0; attempt < config_.max_attempts; ++attempt) { if (auto result = func(); result.is_ok() || - result.err().ec() != error_code::STATEMENT_LOCKED) { + result.err().ec() != error_code::StatementLocked) { return result; } @@ -115,7 +115,7 @@ protected: } return utils::failure(utils::error{ - error_code::STATEMENT_LOCKED, + error_code::StatementLocked, "Failed to execute statement because it is already in use" }); } @@ -167,7 +167,7 @@ utils::result statement_cache::acquire(const query_cont const auto conn = pool_.acquire(); auto result = conn->perform_prepare(ctx); if (!result) { - return utils::failure(utils::error{error_code::PREPARE_FAILED, std::string("Failed to prepare")}); + return utils::failure(utils::error{error_code::PrepareFailed, std::string("Failed to prepare")}); } id = conn.id().value(); stmt = result.release(); diff --git a/test/backends/QueryTest.cpp b/test/backends/QueryTest.cpp index 7041387..d58db76 100644 --- a/test/backends/QueryTest.cpp +++ b/test/backends/QueryTest.cpp @@ -24,8 +24,45 @@ using namespace matador::object; using namespace matador::query; using namespace matador::sql; using namespace matador::test; +using namespace matador::utils; using namespace matador::query::meta; +void initialize_resolver(const basic_schema& repo, connection& db) { + for (const auto &pair : repo.resolver_producers()) { + auto res = pair.second->build_query(db.dialect()).and_then([&db](query_context&& query_ctx) -> result { + query_ctx.resolver = db.resolver(); + return db.prepare(query_ctx); + }).and_then([&pair, &db](statement&& stmt) -> result { + db.resolver()->register_object_resolver(pair.second->produce(std::move(stmt))); + + return ok(); + }).or_else([](const auto &err) { + return failure(err); + }); + + if (!res) { + throw std::runtime_error(res.err().message()); + } + } + + for (const auto &pair : repo.collection_resolver_producers()) { + auto res = pair.second->build_query(db.dialect()).and_then([&db](query_context&& query_ctx) -> result { + query_ctx.resolver = db.resolver(); + return db.prepare(query_ctx); + }).and_then([&pair, &db](statement&& stmt) -> result { + db.resolver()->register_collection_resolver(pair.second->produce(std::move(stmt), *db.resolver())); + + return ok(); + }).or_else([](const auto &err) { + return failure(err); + }); + + if (!res) { + throw std::runtime_error(res.err().message()); + } + } +} + TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") { const auto result = repo.attach("airplane") .and_then([this] { return repo.attach("flight");}) @@ -311,7 +348,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); - // repo.initialize(db); + initialize_resolver(repo, db); REQUIRE(db.exists(AIRPLANE.table_name())); REQUIRE(db.exists(FLIGHT.table_name())); @@ -538,7 +575,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many relation", .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); - // repo.initialize(db); + initialize_resolver(repo, db); const std::vector shipments { make_object(1, "4711"), @@ -636,7 +673,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy has many relation", " } ); REQUIRE(result.is_ok()); - // repo.initialize(db); + initialize_resolver(repo, db); const std::vector authors { make_object(1, "Michael", "Crichton", "23.10.1942", 1975, true), @@ -718,7 +755,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy belongs to relation", .and_then([this] {return repo.create(db); }); REQUIRE(result.is_ok()); - // repo.initialize(db); + initialize_resolver(repo, db); const std::vector deps { make_object(1, "Human Resources"), @@ -864,7 +901,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with eager has many to many rel REQUIRE(result.is_ok()); - // repo.initialize(db); + initialize_resolver(repo, db); REQUIRE(db.exists(RECIPE.table_name())); REQUIRE(db.exists(INGREDIENT.table_name())); diff --git a/test/backends/SessionInsertHasMany.cpp b/test/backends/SessionInsertHasMany.cpp index 82af7ab..719ae40 100644 --- a/test/backends/SessionInsertHasMany.cpp +++ b/test/backends/SessionInsertHasMany.cpp @@ -29,7 +29,7 @@ struct author_identity { void process(Operator &op) { namespace field = matador::access; field::primary_key(op, "id", id, utils::Identity); - field::attribute(op, "first_name", name, VarChar63); + field::attribute(op, "name", name, VarChar63); field::has_many(op, "books", books, "author_id", utils::CascadeAllFetchLazy); } }; diff --git a/test/backends/SessionTest.cpp b/test/backends/SessionTest.cpp index cfd6887..3ecb511 100644 --- a/test/backends/SessionTest.cpp +++ b/test/backends/SessionTest.cpp @@ -88,7 +88,7 @@ TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") { result = ses.find(1); REQUIRE(result.is_error()); - REQUIRE(result.err().ec() == query::error_code::FailedToFindObject); + REQUIRE(result.err().ec() == sql::error_code::FetchFailed); } TEST_CASE_METHOD(SessionFixture, "Session relation test", "[session][relation]") { @@ -125,7 +125,7 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session auto find_result = ses.find(2); REQUIRE(!find_result.is_ok()); - REQUIRE((find_result.err().ec() == query::error_code::FailedToFindObject)); + REQUIRE((find_result.err().ec() == sql::error_code::FetchFailed)); find_result = ses.find(1); diff --git a/test/orm/query/InsertQueryBuilderTest.cpp b/test/orm/query/InsertQueryBuilderTest.cpp index 849250e..1217960 100644 --- a/test/orm/query/InsertQueryBuilderTest.cpp +++ b/test/orm/query/InsertQueryBuilderTest.cpp @@ -38,19 +38,19 @@ std::unordered_map to_contexts_by_name(const sc // SELECT one queries.select_one = select(node.table()) .from(node.name()) - .where(*node.table().primary_key_column().value() == _) + .where(*node.table().primary_key_column() == _) .compile(d); // UPDATE one auto update_set = update(node.table()); for (const auto &col: node.table().columns()) { update_set.set(col, _); } - queries.update_one = update_set.where(*node.table().primary_key_column().value() == _) + queries.update_one = update_set.where(*node.table().primary_key_column() == _) .compile(d); // DELETE one queries.delete_one = remove() .from(node.name()) - .where(*node.table().primary_key_column().value() == _) + .where(*node.table().primary_key_column() == _) .compile(d); } // INSERT one