session progress
This commit is contained in:
parent
656d045ccf
commit
75d5cba6c3
|
|
@ -34,7 +34,7 @@ utils::result<void, utils::error> postgres_connection::open() {
|
||||||
const std::string msg = PQerrorMessage(conn_);
|
const std::string msg = PQerrorMessage(conn_);
|
||||||
PQfinish(conn_);
|
PQfinish(conn_);
|
||||||
conn_ = nullptr;
|
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<void>();
|
return utils::ok<void>();
|
||||||
|
|
@ -70,7 +70,7 @@ utils::result<utils::version, utils::error> postgres_connection::server_version(
|
||||||
const auto server_version = PQserverVersion(conn_);
|
const auto server_version = PQserverVersion(conn_);
|
||||||
|
|
||||||
if (server_version == 0) {
|
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{
|
return utils::ok(utils::version{
|
||||||
|
|
@ -86,7 +86,7 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
|
||||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
PGresult *res = PQexec(conn_, context.sql.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
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);
|
PQclear(res);
|
||||||
return utils::failure(err);
|
return utils::failure(err);
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +95,7 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_co
|
||||||
|
|
||||||
const int num_col = PQnfields(res);
|
const int num_col = PQnfields(res);
|
||||||
if (prototype.size() != static_cast<size_t>(num_col)) {
|
if (prototype.size() != static_cast<size_t>(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);
|
PQclear(res);
|
||||||
return utils::failure(err);
|
return utils::failure(err);
|
||||||
}
|
}
|
||||||
|
|
@ -133,7 +133,7 @@ utils::result<sql::execute_result, utils::error> postgres_connection::execute(co
|
||||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
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<size_t>(PQcmdTuples(res));
|
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||||
|
|
@ -149,7 +149,7 @@ utils::result<std::unique_ptr<sql::statement_impl>, utils::error> postgres_conne
|
||||||
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
|
PGresult *result = PQprepare(conn_, statement_name.c_str(), context.sql.c_str(), static_cast<int>(context.bind_vars.size()), nullptr);
|
||||||
|
|
||||||
if (is_result_error(result)) {
|
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<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
|
std::unique_ptr<sql::statement_impl> s(std::make_unique<postgres_statement>(conn_, result, statement_name, context));
|
||||||
|
|
@ -161,7 +161,7 @@ utils::result<sql::execute_result, utils::error> postgres_connection::execute(co
|
||||||
PGresult *res = PQexec(conn_, context.sql.c_str());
|
PGresult *res = PQexec(conn_, context.sql.c_str());
|
||||||
|
|
||||||
if (const auto status = PQresultStatus(res); status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
|
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<size_t>(PQcmdTuples(res));
|
const size_t affected_rows = utils::to<size_t>(PQcmdTuples(res));
|
||||||
|
|
@ -256,7 +256,7 @@ utils::result<std::vector<object::attribute>, utils::error> postgres_connection:
|
||||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
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);
|
postgres_result_reader reader(res);
|
||||||
|
|
@ -297,13 +297,13 @@ utils::result<bool, utils::error> postgres_connection::exists(const std::string
|
||||||
PGresult *res = PQexec(conn_, stmt.c_str());
|
PGresult *res = PQexec(conn_, stmt.c_str());
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
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<size_t>(PQcmdTuples(res));
|
const auto result = utils::to<size_t>(PQcmdTuples(res));
|
||||||
if (!result) {
|
if (!result) {
|
||||||
PQclear(res);
|
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);
|
PQclear(res);
|
||||||
|
|
@ -315,7 +315,7 @@ utils::result<bool, utils::error> postgres_connection::sequence_exists(const std
|
||||||
PGresult* res = PQexec(conn_, sql.c_str());
|
PGresult* res = PQexec(conn_, sql.c_str());
|
||||||
|
|
||||||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
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;
|
const bool exists = PQgetisnull(res, 0, 0) ? false : true;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ postgres_statement::~postgres_statement() {
|
||||||
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
|
utils::result<sql::execute_result, utils::error> postgres_statement::execute(const sql::parameter_binder& bindings) {
|
||||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||||
if (!postgres_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) {
|
if (query_.command == sql::sql_command::Insert) {
|
||||||
|
|
@ -37,7 +37,7 @@ utils::result<sql::execute_result, utils::error> postgres_statement::execute(con
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
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};
|
size_t value{0};
|
||||||
|
|
@ -53,7 +53,7 @@ utils::result<sql::execute_result, utils::error> postgres_statement::execute(con
|
||||||
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
|
utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_statement::fetch(const sql::parameter_binder& bindings) {
|
||||||
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
const auto* postgres_bindings = dynamic_cast<const postgres_parameter_binder*>(&bindings);
|
||||||
if (!postgres_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_,
|
PGresult *res = PQexecPrepared(db_,
|
||||||
name_.c_str(),
|
name_.c_str(),
|
||||||
|
|
@ -64,7 +64,7 @@ utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> postgres_st
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (is_result_error(res)) {
|
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<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
return utils::ok(std::make_unique<sql::query_result_impl>(std::make_unique<postgres_result_reader>(res),
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,11 @@ private:
|
||||||
|
|
||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
|
void object_generator::on_primary_key(const char *id, ValueType &x, const utils::primary_key_attribute& attr) {
|
||||||
auto &ref = emplace_attribute<ValueType>(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<ValueType>(id, { attr.size(), cs }, null_option_type::NotNull);
|
||||||
prepare_primary_key(ref, utils::identifier(x));
|
prepare_primary_key(ref, utils::identifier(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,11 +89,13 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] bool contains(const std::type_index &index) const;
|
[[nodiscard]] bool contains(const std::type_index &index) const;
|
||||||
|
|
||||||
|
const std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>>& resolver_producers() const;
|
||||||
|
const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::collection_resolver_producer>, object::collection_composite_key_hash>& collection_resolver_producers() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
friend class schema_observer;
|
friend class schema_observer;
|
||||||
friend class producer_creator;
|
friend class producer_creator;
|
||||||
friend class producer_accessor;
|
|
||||||
|
|
||||||
object::repository repo_;
|
object::repository repo_;
|
||||||
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ enum class error_code {
|
||||||
InvalidColumn,
|
InvalidColumn,
|
||||||
InvalidConstraint,
|
InvalidConstraint,
|
||||||
InvalidDataType,
|
InvalidDataType,
|
||||||
|
InvalidObject,
|
||||||
InvalidValue,
|
InvalidValue,
|
||||||
InvalidRelationType,
|
InvalidRelationType,
|
||||||
MissingPrimaryKey,
|
MissingPrimaryKey,
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,11 @@ public:
|
||||||
visited_.clear();
|
visited_.clear();
|
||||||
|
|
||||||
ptr_ = ptr;
|
ptr_ = ptr;
|
||||||
build_for(ptr, steps_);
|
const auto result = build_for(ptr, steps_);
|
||||||
ptr_.reset();
|
ptr_.reset();
|
||||||
|
if (!result) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
// relation inserts must run after all entity inserts were collected
|
// relation inserts must run after all entity inserts were collected
|
||||||
for (auto &s : relation_steps_) {
|
for (auto &s : relation_steps_) {
|
||||||
|
|
@ -151,8 +154,7 @@ public:
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
||||||
}
|
}
|
||||||
|
|
||||||
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
||||||
|
|
||||||
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
|
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
|
||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
||||||
}
|
}
|
||||||
|
|
@ -162,7 +164,7 @@ public:
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
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<insert_step> rel_steps;
|
std::vector<insert_step> rel_steps;
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
|
@ -174,7 +176,7 @@ public:
|
||||||
build_for(obj, relation_steps_);
|
build_for(obj, relation_steps_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rel = object::make_object<relation_value_type>(join_column, inverse_join_column, obj, ptr_);
|
auto rel = object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
||||||
|
|
||||||
access::process(*this, *rel);
|
access::process(*this, *rel);
|
||||||
|
|
||||||
|
|
@ -219,7 +221,7 @@ public:
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
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<insert_step> rel_steps;
|
std::vector<insert_step> rel_steps;
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
|
@ -263,24 +265,28 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class EntityType>
|
template<class EntityType>
|
||||||
void build_for(const object::object_ptr<EntityType> &ptr, std::vector<insert_step> &steps) {
|
utils::result<void, utils::error> build_for(const object::object_ptr<EntityType> &ptr, std::vector<insert_step> &steps) {
|
||||||
if (!ptr) {
|
if (!ptr) {
|
||||||
return;
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto key = make_visit_key<EntityType>(ptr);
|
const auto key = make_visit_key<EntityType>(ptr);
|
||||||
if (visited_.find(key) != visited_.end()) {
|
if (visited_.find(key) != visited_.end()) {
|
||||||
return;
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
visited_.insert(key);
|
visited_.insert(key);
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(EntityType));
|
const auto it = schema_.find(typeid(EntityType));
|
||||||
if (it == schema_.end()) {
|
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
|
// 1) Traverse relations first => dependencies will be inserted before this object
|
||||||
|
try {
|
||||||
access::process(*this, *ptr);
|
access::process(*this, *ptr);
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
// 2) Build INSERT for this object
|
// 2) Build INSERT for this object
|
||||||
const auto &info = it->second.node().info();
|
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());
|
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
if (cit == contexts_by_type_.end()) {
|
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.ctx = cit->second.insert;
|
||||||
step.bind_object = [ptr](sql::statement &stmt) { stmt.bind(*ptr); };
|
step.bind_object = [ptr](sql::statement &stmt) { stmt.bind(*ptr); };
|
||||||
if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) {
|
if (info.has_primary_key() && step.pk_generator == utils::generator_type::Identity) {
|
||||||
const auto pk_name = info.primary_key_attribute()->name();
|
const auto pk_name = info.primary_key_attribute()->name();
|
||||||
const table_column pk_col(&it->second.table(), pk_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) {
|
step.apply_returning = [ptr, &step, pk_name = pk_name](const sql::record &rec) {
|
||||||
const auto& f = rec.at(pk_name);
|
const auto& f = rec.at(pk_name);
|
||||||
utils::identifier id;
|
utils::identifier id;
|
||||||
id.assign(f.value());
|
id.assign(f.value());
|
||||||
step.pk_accessor.set(*ptr, id);
|
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.apply_primary_key = [ptr, &step](const utils::identifier &id) {
|
||||||
step.pk_accessor.set(*ptr, id);
|
step.pk_accessor.set(*ptr, id);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
step.make_object_persistent = [ptr] { ptr.change_state(object::object_state::Persistent); };
|
step.make_object_persistent = [ptr] { ptr.change_state(object::object_state::Persistent); };
|
||||||
steps.push_back(std::move(step));
|
steps.push_back(std::move(step));
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
template<class Pointer>
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,10 @@
|
||||||
#include "matador/query/query.hpp"
|
#include "matador/query/query.hpp"
|
||||||
#include "matador/query/query_contexts.hpp"
|
#include "matador/query/query_contexts.hpp"
|
||||||
#include "matador/query/generator.hpp"
|
#include "matador/query/generator.hpp"
|
||||||
#include "matador/query/schema.hpp"
|
|
||||||
|
|
||||||
#include "matador/sql/connection.hpp"
|
#include "matador/sql/connection.hpp"
|
||||||
#include "matador/sql/connection_pool.hpp"
|
#include "matador/sql/connection_pool.hpp"
|
||||||
#include "matador/sql/executor.hpp"
|
#include "matador/sql/executor.hpp"
|
||||||
#include "matador/sql/record.hpp"
|
|
||||||
#include "matador/sql/resolver_service.hpp"
|
#include "matador/sql/resolver_service.hpp"
|
||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
#include "matador/sql/statement_cache.hpp"
|
#include "matador/sql/statement_cache.hpp"
|
||||||
|
|
@ -42,7 +40,7 @@ struct session_context {
|
||||||
|
|
||||||
class session final /*: public sql::executor*/ {
|
class session final /*: public sql::executor*/ {
|
||||||
public:
|
public:
|
||||||
session(session_context &&ctx, const query::schema &scm);
|
session(session_context &&ctx, const basic_schema &scm);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the given object into the session.
|
* Insert the given object into the session.
|
||||||
|
|
@ -64,20 +62,14 @@ public:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<sql::query_result<Type>, utils::error> find(query::criteria_ptr clause = {});
|
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]] const basic_schema &schema() const;
|
||||||
// [[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:
|
private:
|
||||||
sql::connection_pool pool_;
|
sql::connection_pool pool_;
|
||||||
mutable sql::statement_cache cache_;
|
mutable sql::statement_cache cache_;
|
||||||
const sql::dialect &dialect_;
|
const sql::dialect &dialect_;
|
||||||
|
|
||||||
const query::basic_schema &schema_;
|
const basic_schema &schema_;
|
||||||
mutable std::unordered_map<std::string, std::vector<object::attribute> > prototypes_;
|
mutable std::unordered_map<std::string, std::vector<object::attribute> > prototypes_;
|
||||||
std::shared_ptr<sql::resolver_service> resolver_service_;
|
std::shared_ptr<sql::resolver_service> resolver_service_;
|
||||||
std::unordered_map<std::type_index, query::query_contexts> contexts_by_type_;
|
std::unordered_map<std::type_index, query::query_contexts> contexts_by_type_;
|
||||||
|
|
@ -257,28 +249,26 @@ utils::result<object::object_ptr<Type>, utils::error> session::find(const Primar
|
||||||
if (it == schema_.end()) {
|
if (it == schema_.end()) {
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
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."));
|
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());
|
select_query_builder eqb(schema_);
|
||||||
if (cit == contexts_by_type_.end()) {
|
auto data = eqb.build<Type>(*it->second.table().primary_key_column() == pk);
|
||||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
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()) {
|
if (!stmt.is_ok()) {
|
||||||
return utils::failure(stmt.err());
|
return utils::failure(stmt.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stmt_result = stmt->bind(0, const_cast<PrimaryKeyType &>(pk))
|
return stmt->template fetch_one<Type>();
|
||||||
.template fetch_one<Type>();
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
|
|
@ -296,6 +286,7 @@ utils::result<sql::query_result<Type>, utils::error> session::find(query::criter
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ctx = data->compile(dialect_);
|
auto ctx = data->compile(dialect_);
|
||||||
|
ctx.resolver = resolver_service_;
|
||||||
auto stmt = cache_.acquire(ctx);
|
auto stmt = cache_.acquire(ctx);
|
||||||
if (!stmt.is_ok()) {
|
if (!stmt.is_ok()) {
|
||||||
return utils::failure(stmt.err());
|
return utils::failure(stmt.err());
|
||||||
|
|
|
||||||
|
|
@ -7,23 +7,23 @@
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
enum class error_code : uint8_t {
|
enum class error_code : uint8_t {
|
||||||
OK = 0,
|
Ok = 0,
|
||||||
INVALID_QUERY,
|
InvalidQuery,
|
||||||
UNKNOWN_TABLE,
|
UnknownTable,
|
||||||
UNKNOWN_COLUMN,
|
UnknownColumn,
|
||||||
BIND_FAILED,
|
BindFailed,
|
||||||
EXECUTE_FAILED,
|
ExecuteFailed,
|
||||||
FETCH_FAILED,
|
FetchFailed,
|
||||||
PREPARE_FAILED,
|
PrepareFailed,
|
||||||
DESCRIBE_FAILED,
|
DescribeFailed,
|
||||||
TABLE_EXISTS_FAILED,
|
TableExistsFailed,
|
||||||
RETRIEVE_DATA_FAILED,
|
RetrieveDataFailed,
|
||||||
SEQUENCE_EXISTS_FAILED,
|
SequenceExistsFailed,
|
||||||
RESET_FAILED,
|
ResetFailed,
|
||||||
OPEN_ERROR,
|
OpenError,
|
||||||
CLOSE_ERROR,
|
CloseError,
|
||||||
STATEMENT_LOCKED,
|
StatementLocked,
|
||||||
FAILURE
|
Failure
|
||||||
};
|
};
|
||||||
|
|
||||||
class sql_category_impl final : public std::error_category
|
class sql_category_impl final : public std::error_category
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,9 @@ public:
|
||||||
static void on_base(const BaseType&) {}
|
static void on_base(const BaseType&) {}
|
||||||
template < class Type >
|
template < class Type >
|
||||||
void on_primary_key(const char * /*id*/, Type &val, const utils::primary_key_attribute& attr) {
|
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<Type>::bind_value(*binder_, index_++, val, attr.size());
|
utils::data_type_traits<Type>::bind_value(*binder_, index_++, val, attr.size());
|
||||||
}
|
}
|
||||||
void on_revision(const char *id, uint64_t &/*rev*/);
|
void on_revision(const char *id, uint64_t &/*rev*/);
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ struct query_context {
|
||||||
std::string table_name{};
|
std::string table_name{};
|
||||||
std::vector<object::attribute> prototype{};
|
std::vector<object::attribute> prototype{};
|
||||||
std::vector<std::string> bind_vars{};
|
std::vector<std::string> bind_vars{};
|
||||||
std::vector<utils::database_type> bind_types{};
|
// std::vector<utils::database_type> bind_types{};
|
||||||
// Data for resolving query result
|
// Data for resolving query result
|
||||||
std::shared_ptr<resolver_service> resolver{};
|
std::shared_ptr<resolver_service> resolver{};
|
||||||
std::type_index result_type = typeid(void);
|
std::type_index result_type = typeid(void);
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
||||||
});
|
});
|
||||||
auto first = records.begin();
|
auto first = records.begin();
|
||||||
if (first == records.end()) {
|
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());
|
return utils::ok(first.optr());
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
bool basic_schema::contains(const std::type_index &index) const {
|
||||||
return schema_nodes_.count(index) == 1;
|
return schema_nodes_.count(index) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>> & basic_schema::resolver_producers() const {
|
||||||
|
return resolver_producers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::collection_resolver_producer>, object::collection_composite_key_hash> & basic_schema::collection_resolver_producers() const {
|
||||||
|
return collection_resolver_producers_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -52,7 +52,9 @@ template<class... Ts> struct overload : Ts... { using Ts::operator()...; };
|
||||||
template<class... Ts> overload(Ts...) -> overload<Ts...>;
|
template<class... Ts> overload(Ts...) -> overload<Ts...>;
|
||||||
|
|
||||||
void criteria_evaluator::visit(const binary_criteria &node) {
|
void criteria_evaluator::visit(const binary_criteria &node) {
|
||||||
|
if (std::holds_alternative<utils::placeholder>(node.value())) {
|
||||||
query_.bind_vars.emplace_back(node.col().name());
|
query_.bind_vars.emplace_back(node.col().name());
|
||||||
|
}
|
||||||
clause_ += prepare_criteria(dialect_, node.col()) + " " + detail::BinaryOperatorEnum.to_string(node.operand()) + " ";
|
clause_ += prepare_criteria(dialect_, node.col()) + " " + detail::BinaryOperatorEnum.to_string(node.operand()) + " ";
|
||||||
|
|
||||||
evaluate_value(node.value());
|
evaluate_value(node.value());
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ std::string query_category_impl::message(int ev) const {
|
||||||
return "Invalid constraint";
|
return "Invalid constraint";
|
||||||
case error_code::InvalidDataType:
|
case error_code::InvalidDataType:
|
||||||
return "Invalid data type";
|
return "Invalid data type";
|
||||||
|
case error_code::InvalidObject:
|
||||||
|
return "Invalid object";
|
||||||
case error_code::InvalidValue:
|
case error_code::InvalidValue:
|
||||||
return "Invalid value";
|
return "Invalid value";
|
||||||
case error_code::InvalidRelationType:
|
case error_code::InvalidRelationType:
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,10 @@ manual_pk_generator::manual_pk_generator()
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<int64_t, utils::error> manual_pk_generator::next_id(const sql::executor &/*exec*/) {
|
utils::result<int64_t, utils::error> 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<int64_t, utils::error> manual_pk_generator::current_id(const sql::executor& /*exec*/) {
|
utils::result<int64_t, utils::error> 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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -357,7 +357,7 @@ void query_builder::visit(internal::query_set_part& part) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
query_.sql.append(", ");
|
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()));
|
query_.sql.append(determine_value(*dialect_, query_, column_value.expression()));
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ sequence_pk_generator::sequence_pk_generator(const std::string& sequence_name)
|
||||||
utils::result<int64_t, utils::error> sequence_pk_generator::next_id(const sql::executor& exec) {
|
utils::result<int64_t, utils::error> sequence_pk_generator::next_id(const sql::executor& exec) {
|
||||||
return next_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
return next_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
||||||
if (!id) {
|
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);
|
return utils::ok(*id);
|
||||||
});
|
});
|
||||||
|
|
@ -22,7 +22,7 @@ utils::result<int64_t, utils::error> sequence_pk_generator::next_id(const sql::e
|
||||||
utils::result<int64_t, utils::error> sequence_pk_generator::current_id(const sql::executor& exec) {
|
utils::result<int64_t, utils::error> sequence_pk_generator::current_id(const sql::executor& exec) {
|
||||||
return current_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
return current_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
||||||
if (!id) {
|
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);
|
return utils::ok(*id);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,11 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
class producer_accessor {
|
|
||||||
public:
|
|
||||||
static const std::unordered_map<std::type_index, std::unique_ptr<sql::object_resolver_producer>>& resolver_producers(const basic_schema& scm) {
|
|
||||||
return scm.resolver_producers_;
|
|
||||||
}
|
|
||||||
static const std::unordered_map<object::collection_composite_key, std::unique_ptr<sql::collection_resolver_producer>, 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) {
|
utils::error make_error(const error_code ec, const std::string &msg) {
|
||||||
return utils::error(ec, 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); })
|
: 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)
|
, cache_(ctx.bus, pool_, ctx.cache_size)
|
||||||
, dialect_(sql::backend_provider::instance().connection_dialect(pool_.info().type))
|
, 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) {
|
, resolver_service_(ctx.resolver_service) {
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
for (const auto &[type, node] : schema_) {
|
for (const auto &[type, node] : schema_) {
|
||||||
query::query_contexts queries;
|
query_contexts queries;
|
||||||
|
|
||||||
// SELECT all
|
// SELECT all
|
||||||
queries.select_all = query::select(node.table())
|
queries.select_all = select(node.table())
|
||||||
.from(node.name())
|
.from(node.name())
|
||||||
.compile(dialect_);
|
.compile(dialect_);
|
||||||
if (node.table().has_primary_key()) {
|
if (node.table().has_primary_key()) {
|
||||||
// SELECT one
|
// SELECT one
|
||||||
queries.select_one = query::select(node.table())
|
queries.select_one = select(node.table())
|
||||||
.from(node.name())
|
.from(node.name())
|
||||||
.where(*node.table().primary_key_column() == _)
|
.where(*node.table().primary_key_column() == _)
|
||||||
.compile(dialect_);
|
.compile(dialect_);
|
||||||
|
|
@ -58,9 +48,16 @@ session::session(session_context&& ctx, const query::schema &scm)
|
||||||
.compile(dialect_);
|
.compile(dialect_);
|
||||||
}
|
}
|
||||||
// INSERT one
|
// INSERT one
|
||||||
|
std::vector<table_column> 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()
|
queries.insert = query::insert()
|
||||||
.into(node.name(), node.table())
|
.into(node.name(), columns)
|
||||||
.values(query::generator::placeholders(node.table().columns().size()))
|
.values(generator::placeholders(columns.size()))
|
||||||
.compile(dialect_);
|
.compile(dialect_);
|
||||||
|
|
||||||
queries.insert.resolver = resolver_service_;
|
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;
|
contexts_by_type_[node.node().type_index()] = queries;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto factory = std::make_shared<sql::producer_resolver_factory>();
|
for (const auto &pair : schema_.resolver_producers()) {
|
||||||
for (const auto &pair : producer_accessor::resolver_producers(schema_)) {
|
|
||||||
auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result<sql::statement, error> {
|
auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result<sql::statement, error> {
|
||||||
query_ctx.resolver = resolver_service_;
|
query_ctx.resolver = resolver_service_;
|
||||||
return cache_.acquire(query_ctx);
|
return cache_.acquire(query_ctx);
|
||||||
|
|
@ -89,8 +85,7 @@ session::session(session_context&& ctx, const query::schema &scm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto collection_factory = std::make_shared<sql::producer_collection_resolver_factory>();
|
for (const auto &pair : schema_.collection_resolver_producers()) {
|
||||||
for (const auto &pair : producer_accessor::collection_resolver_producers(schema_)) {
|
|
||||||
auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result<sql::statement, error> {
|
auto res = pair.second->build_query(dialect_).and_then([this](sql::query_context&& query_ctx) -> result<sql::statement, error> {
|
||||||
query_ctx.resolver = resolver_service_;
|
query_ctx.resolver = resolver_service_;
|
||||||
return cache_.acquire(query_ctx);
|
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 {
|
const basic_schema & session::schema() const {
|
||||||
// return dialect_;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// std::shared_ptr<sql::resolver_service> session::resolver() const {
|
|
||||||
// return resolver_service_;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const query::basic_schema & session::schema() const {
|
|
||||||
return schema_;
|
return schema_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// utils::result<std::unique_ptr<sql::query_result_impl>, 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<sql::execute_result, utils::error> 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<sql::statement, utils::error> session::prepare(const sql::query_context& ctx) {
|
|
||||||
// return cache_.acquire(ctx);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// std::string session::str(const sql::query_context& ctx) const {
|
|
||||||
// return ctx.sql;
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ table_pk_generator::table_pk_generator(const std::string& table_name, const std:
|
||||||
utils::result<int64_t, utils::error> table_pk_generator::next_id(const sql::executor &exec) {
|
utils::result<int64_t, utils::error> table_pk_generator::next_id(const sql::executor &exec) {
|
||||||
return next_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
return next_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
||||||
if (!id) {
|
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);
|
return utils::ok(*id);
|
||||||
});
|
});
|
||||||
|
|
@ -30,7 +30,7 @@ utils::result<int64_t, utils::error> table_pk_generator::next_id(const sql::exec
|
||||||
utils::result<int64_t, utils::error> table_pk_generator::current_id(const sql::executor& exec) {
|
utils::result<int64_t, utils::error> table_pk_generator::current_id(const sql::executor& exec) {
|
||||||
return current_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
return current_id_query_.fetch_value<int64_t>(exec).and_then([](const std::optional<int64_t> id) -> utils::result<int64_t, utils::error> {
|
||||||
if (!id) {
|
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);
|
return utils::ok(*id);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -8,37 +8,37 @@ const char * sql_category_impl::name() const noexcept {
|
||||||
|
|
||||||
std::string sql_category_impl::message(const int ev) const {
|
std::string sql_category_impl::message(const int ev) const {
|
||||||
switch (static_cast<error_code>(ev)) {
|
switch (static_cast<error_code>(ev)) {
|
||||||
case error_code::OK:
|
case error_code::Ok:
|
||||||
return "OK";
|
return "OK";
|
||||||
case error_code::INVALID_QUERY:
|
case error_code::InvalidQuery:
|
||||||
return "Invalid query";
|
return "Invalid query";
|
||||||
case error_code::UNKNOWN_TABLE:
|
case error_code::UnknownTable:
|
||||||
return "Unknown table";
|
return "Unknown table";
|
||||||
case error_code::UNKNOWN_COLUMN:
|
case error_code::UnknownColumn:
|
||||||
return "Unknown column";
|
return "Unknown column";
|
||||||
case error_code::BIND_FAILED:
|
case error_code::BindFailed:
|
||||||
return "Bind failed";
|
return "Bind failed";
|
||||||
case error_code::EXECUTE_FAILED:
|
case error_code::ExecuteFailed:
|
||||||
return "Execute failed";
|
return "Execute failed";
|
||||||
case error_code::FETCH_FAILED:
|
case error_code::FetchFailed:
|
||||||
return "Fetch failed";
|
return "Fetch failed";
|
||||||
case error_code::PREPARE_FAILED:
|
case error_code::PrepareFailed:
|
||||||
return "Prepare failed";
|
return "Prepare failed";
|
||||||
case error_code::DESCRIBE_FAILED:
|
case error_code::DescribeFailed:
|
||||||
return "Describe failed";
|
return "Describe failed";
|
||||||
case error_code::TABLE_EXISTS_FAILED:
|
case error_code::TableExistsFailed:
|
||||||
return "Table exists failed";
|
return "Table exists failed";
|
||||||
case error_code::RETRIEVE_DATA_FAILED:
|
case error_code::RetrieveDataFailed:
|
||||||
return "Retrieve data failed";
|
return "Retrieve data failed";
|
||||||
case error_code::SEQUENCE_EXISTS_FAILED:
|
case error_code::SequenceExistsFailed:
|
||||||
return "Sequence exists failed";
|
return "Sequence exists failed";
|
||||||
case error_code::RESET_FAILED:
|
case error_code::ResetFailed:
|
||||||
return "Reset failed";
|
return "Reset failed";
|
||||||
case error_code::OPEN_ERROR:
|
case error_code::OpenError:
|
||||||
return "Open failed";
|
return "Open failed";
|
||||||
case error_code::CLOSE_ERROR:
|
case error_code::CloseError:
|
||||||
return "Close failed";
|
return "Close failed";
|
||||||
case error_code::FAILURE:
|
case error_code::Failure:
|
||||||
return "Failure";
|
return "Failure";
|
||||||
default:
|
default:
|
||||||
return "Unknown error";
|
return "Unknown error";
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ public:
|
||||||
const auto now = std::chrono::steady_clock::now();
|
const auto now = std::chrono::steady_clock::now();
|
||||||
bus_.publish(statement_lock_failed_event{query, now, now - metrics.lock_attempt_start});
|
bus_.publish(statement_lock_failed_event{query, now, now - metrics.lock_attempt_start});
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{
|
||||||
error_code::STATEMENT_LOCKED,
|
error_code::StatementLocked,
|
||||||
"Failed to execute statement because it is already in use"
|
"Failed to execute statement because it is already in use"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +51,7 @@ public:
|
||||||
auto guard = statement_guard(*this);
|
auto guard = statement_guard(*this);
|
||||||
if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) {
|
if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) {
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{
|
||||||
error_code::EXECUTE_FAILED,
|
error_code::ExecuteFailed,
|
||||||
"Failed to execute statement because couldn't lock connection"
|
"Failed to execute statement because couldn't lock connection"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -75,14 +75,14 @@ public:
|
||||||
if (!try_lock()) {
|
if (!try_lock()) {
|
||||||
++metrics.lock_attempts;
|
++metrics.lock_attempts;
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{
|
||||||
error_code::STATEMENT_LOCKED,
|
error_code::StatementLocked,
|
||||||
"Failed to execute statement because it is already in use"
|
"Failed to execute statement because it is already in use"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
auto guard = statement_guard(*this);
|
auto guard = statement_guard(*this);
|
||||||
if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) {
|
if (const auto conn = pool_.acquire(connection_id_); !conn.valid()) {
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{
|
||||||
error_code::EXECUTE_FAILED,
|
error_code::ExecuteFailed,
|
||||||
"Failed to execute statement because couldn't lock connection"
|
"Failed to execute statement because couldn't lock connection"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +104,7 @@ protected:
|
||||||
|
|
||||||
for (size_t attempt = 0; attempt < config_.max_attempts; ++attempt) {
|
for (size_t attempt = 0; attempt < config_.max_attempts; ++attempt) {
|
||||||
if (auto result = func(); result.is_ok() ||
|
if (auto result = func(); result.is_ok() ||
|
||||||
result.err().ec() != error_code::STATEMENT_LOCKED) {
|
result.err().ec() != error_code::StatementLocked) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -115,7 +115,7 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
return utils::failure(utils::error{
|
return utils::failure(utils::error{
|
||||||
error_code::STATEMENT_LOCKED,
|
error_code::StatementLocked,
|
||||||
"Failed to execute statement because it is already in use"
|
"Failed to execute statement because it is already in use"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +167,7 @@ utils::result<statement, utils::error> statement_cache::acquire(const query_cont
|
||||||
const auto conn = pool_.acquire();
|
const auto conn = pool_.acquire();
|
||||||
auto result = conn->perform_prepare(ctx);
|
auto result = conn->perform_prepare(ctx);
|
||||||
if (!result) {
|
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();
|
id = conn.id().value();
|
||||||
stmt = result.release();
|
stmt = result.release();
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,45 @@ using namespace matador::object;
|
||||||
using namespace matador::query;
|
using namespace matador::query;
|
||||||
using namespace matador::sql;
|
using namespace matador::sql;
|
||||||
using namespace matador::test;
|
using namespace matador::test;
|
||||||
|
using namespace matador::utils;
|
||||||
using namespace matador::query::meta;
|
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<statement, error> {
|
||||||
|
query_ctx.resolver = db.resolver();
|
||||||
|
return db.prepare(query_ctx);
|
||||||
|
}).and_then([&pair, &db](statement&& stmt) -> result<void, error> {
|
||||||
|
db.resolver()->register_object_resolver(pair.second->produce(std::move(stmt)));
|
||||||
|
|
||||||
|
return ok<void>();
|
||||||
|
}).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<statement, error> {
|
||||||
|
query_ctx.resolver = db.resolver();
|
||||||
|
return db.prepare(query_ctx);
|
||||||
|
}).and_then([&pair, &db](statement&& stmt) -> result<void, error> {
|
||||||
|
db.resolver()->register_collection_resolver(pair.second->produce(std::move(stmt), *db.resolver()));
|
||||||
|
|
||||||
|
return ok<void>();
|
||||||
|
}).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]") {
|
TEST_CASE_METHOD(QueryFixture, "Create table with foreign key relation", "[query][foreign][relation]") {
|
||||||
const auto result = repo.attach<airplane>("airplane")
|
const auto result = repo.attach<airplane>("airplane")
|
||||||
.and_then([this] { return repo.attach<flight>("flight");})
|
.and_then([this] { return repo.attach<flight>("flight");})
|
||||||
|
|
@ -311,7 +348,7 @@ TEST_CASE_METHOD(QueryFixture, "Select statement with foreign key and for single
|
||||||
.and_then([this] {return repo.create(db); });
|
.and_then([this] {return repo.create(db); });
|
||||||
REQUIRE(result.is_ok());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// repo.initialize(db);
|
initialize_resolver(repo, db);
|
||||||
|
|
||||||
REQUIRE(db.exists(AIRPLANE.table_name()));
|
REQUIRE(db.exists(AIRPLANE.table_name()));
|
||||||
REQUIRE(db.exists(FLIGHT.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); });
|
.and_then([this] {return repo.create(db); });
|
||||||
REQUIRE(result.is_ok());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// repo.initialize(db);
|
initialize_resolver(repo, db);
|
||||||
|
|
||||||
const std::vector shipments {
|
const std::vector shipments {
|
||||||
make_object<shipment>(1, "4711"),
|
make_object<shipment>(1, "4711"),
|
||||||
|
|
@ -636,7 +673,7 @@ TEST_CASE_METHOD(QueryFixture, "Test load entity with lazy has many relation", "
|
||||||
} );
|
} );
|
||||||
REQUIRE(result.is_ok());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// repo.initialize(db);
|
initialize_resolver(repo, db);
|
||||||
|
|
||||||
const std::vector authors {
|
const std::vector authors {
|
||||||
make_object<author>(1, "Michael", "Crichton", "23.10.1942", 1975, true),
|
make_object<author>(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); });
|
.and_then([this] {return repo.create(db); });
|
||||||
REQUIRE(result.is_ok());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// repo.initialize(db);
|
initialize_resolver(repo, db);
|
||||||
|
|
||||||
const std::vector deps {
|
const std::vector deps {
|
||||||
make_object<department>(1, "Human Resources"),
|
make_object<department>(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());
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
// repo.initialize(db);
|
initialize_resolver(repo, db);
|
||||||
|
|
||||||
REQUIRE(db.exists(RECIPE.table_name()));
|
REQUIRE(db.exists(RECIPE.table_name()));
|
||||||
REQUIRE(db.exists(INGREDIENT.table_name()));
|
REQUIRE(db.exists(INGREDIENT.table_name()));
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ struct author_identity {
|
||||||
void process(Operator &op) {
|
void process(Operator &op) {
|
||||||
namespace field = matador::access;
|
namespace field = matador::access;
|
||||||
field::primary_key(op, "id", id, utils::Identity);
|
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);
|
field::has_many(op, "books", books, "author_id", utils::CascadeAllFetchLazy);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ TEST_CASE_METHOD(SessionFixture, "Session delete test", "[session][delete]") {
|
||||||
|
|
||||||
result = ses.find<airplane>(1);
|
result = ses.find<airplane>(1);
|
||||||
REQUIRE(result.is_error());
|
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]") {
|
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<airplane>(2);
|
auto find_result = ses.find<airplane>(2);
|
||||||
REQUIRE(!find_result.is_ok());
|
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<airplane>(1);
|
find_result = ses.find<airplane>(1);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,19 +38,19 @@ std::unordered_map<std::type_index, query_contexts> to_contexts_by_name(const sc
|
||||||
// SELECT one
|
// SELECT one
|
||||||
queries.select_one = select(node.table())
|
queries.select_one = select(node.table())
|
||||||
.from(node.name())
|
.from(node.name())
|
||||||
.where(*node.table().primary_key_column().value() == _)
|
.where(*node.table().primary_key_column() == _)
|
||||||
.compile(d);
|
.compile(d);
|
||||||
// UPDATE one
|
// UPDATE one
|
||||||
auto update_set = update(node.table());
|
auto update_set = update(node.table());
|
||||||
for (const auto &col: node.table().columns()) {
|
for (const auto &col: node.table().columns()) {
|
||||||
update_set.set(col, _);
|
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);
|
.compile(d);
|
||||||
// DELETE one
|
// DELETE one
|
||||||
queries.delete_one = remove()
|
queries.delete_one = remove()
|
||||||
.from(node.name())
|
.from(node.name())
|
||||||
.where(*node.table().primary_key_column().value() == _)
|
.where(*node.table().primary_key_column() == _)
|
||||||
.compile(d);
|
.compile(d);
|
||||||
}
|
}
|
||||||
// INSERT one
|
// INSERT one
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue