use result in entity_query_builder
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
#include "matador/sql/query_intermediates.hpp"
|
||||
#include "matador/sql/value.hpp"
|
||||
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace matador::sql {
|
||||
@@ -25,6 +27,24 @@ struct entity_query_data {
|
||||
std::unique_ptr<basic_condition> where_clause;
|
||||
};
|
||||
|
||||
enum class query_build_error : std::uint8_t {
|
||||
Ok = 0,
|
||||
UnknownType,
|
||||
MissingPrimaryKey,
|
||||
UnexpectedError
|
||||
};
|
||||
|
||||
class query_builder_exception : public std::exception
|
||||
{
|
||||
public:
|
||||
explicit query_builder_exception(query_build_error error) : error_(error) {}
|
||||
|
||||
[[nodiscard]] query_build_error error() const { return error_; }
|
||||
|
||||
private:
|
||||
const query_build_error error_;
|
||||
};
|
||||
|
||||
class entity_query_builder
|
||||
{
|
||||
public:
|
||||
@@ -32,18 +52,45 @@ public:
|
||||
: schema_(scm) {}
|
||||
|
||||
template<class EntityType, typename PrimaryKeyType>
|
||||
std::optional<entity_query_data> build(const PrimaryKeyType &pk) {
|
||||
utils::result<entity_query_data, query_build_error> build(const PrimaryKeyType &pk) {
|
||||
const auto info = schema_.info<EntityType>();
|
||||
if (!info) {
|
||||
return std::nullopt;
|
||||
return utils::error(query_build_error::UnknownType);
|
||||
}
|
||||
pk_ = pk;
|
||||
table_info_stack_.push(info.value());
|
||||
entity_query_data_ = { info->name };
|
||||
EntityType obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
try {
|
||||
matador::utils::access::process(*this, obj);
|
||||
|
||||
return std::move(entity_query_data_);
|
||||
return {utils::ok(std::move(entity_query_data_))};
|
||||
} catch (const query_builder_exception &ex) {
|
||||
return {utils::error(ex.error())};
|
||||
} catch (...) {
|
||||
return {utils::error(query_build_error::UnexpectedError)};
|
||||
}
|
||||
}
|
||||
|
||||
template<class EntityType>
|
||||
utils::result<entity_query_data, query_build_error> build() {
|
||||
const auto info = schema_.info<EntityType>();
|
||||
if (!info) {
|
||||
return utils::error(query_build_error::UnknownType);
|
||||
}
|
||||
pk_ = nullptr;
|
||||
table_info_stack_.push(info.value());
|
||||
entity_query_data_ = { info->name };
|
||||
EntityType obj;
|
||||
try {
|
||||
matador::utils::access::process(*this, obj);
|
||||
|
||||
return {utils::ok(std::move(entity_query_data_))};
|
||||
} catch (const query_builder_exception &ex) {
|
||||
return {utils::error(ex.error())};
|
||||
} catch (...) {
|
||||
return {utils::error(query_build_error::UnexpectedError)};
|
||||
}
|
||||
}
|
||||
|
||||
template < class V >
|
||||
@@ -65,51 +112,15 @@ public:
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
void on_belongs_to(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this , obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
|
||||
} else {
|
||||
push(id);
|
||||
}
|
||||
on_foreign_object(id, obj, attr);
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_has_one(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
void on_has_one(const char *id, Pointer &obj, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
}
|
||||
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
|
||||
} else {
|
||||
push(id);
|
||||
}
|
||||
on_foreign_object(id, obj, attr);
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
@@ -118,7 +129,7 @@ public:
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
throw query_builder_exception{query_build_error::UnknownType};
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
@@ -127,8 +138,7 @@ public:
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
||||
}
|
||||
|
||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column});
|
||||
@@ -141,7 +151,7 @@ public:
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||
if (!info) {
|
||||
return;
|
||||
throw query_builder_exception{query_build_error::UnknownType};
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename ContainerType::value_type::value_type obj;
|
||||
@@ -150,8 +160,7 @@ public:
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
// error, prototype doesn't has a pk
|
||||
return;
|
||||
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
||||
}
|
||||
|
||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column});
|
||||
@@ -163,6 +172,8 @@ public:
|
||||
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
template<class Pointer>
|
||||
void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr);
|
||||
void push(const std::string &column_name);
|
||||
[[nodiscard]] bool is_root_entity() const;
|
||||
void append_join(const column &left, const column &right);
|
||||
@@ -175,5 +186,28 @@ private:
|
||||
int column_index{0};
|
||||
};
|
||||
|
||||
template<class Pointer>
|
||||
void entity_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr)
|
||||
{
|
||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||
const auto info = schema_.info<typename Pointer::value_type>();
|
||||
if (!info) {
|
||||
throw query_builder_exception{query_build_error::UnknownType};
|
||||
}
|
||||
table_info_stack_.push(info.value());
|
||||
typename Pointer::value_type obj;
|
||||
matador::utils::access::process(*this, obj);
|
||||
table_info_stack_.pop();
|
||||
|
||||
auto pk = info->prototype.primary_key();
|
||||
if (!pk) {
|
||||
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
||||
}
|
||||
append_join({table_info_stack_.top().name, id}, {info->name, pk->name()});
|
||||
} else {
|
||||
push(id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif //QUERY_ENTITY_QUERY_BUILDER_HPP
|
||||
|
||||
@@ -40,7 +40,7 @@ protected:
|
||||
std::shared_ptr<query_data> data_;
|
||||
};
|
||||
|
||||
class query_execute_finish : public query_intermediate
|
||||
class query_execute : public query_intermediate
|
||||
{
|
||||
public:
|
||||
using query_intermediate::query_intermediate;
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
[[nodiscard]] query_context build() const;
|
||||
};
|
||||
|
||||
class query_select_finish : public query_intermediate
|
||||
class query_select : public query_intermediate
|
||||
{
|
||||
protected:
|
||||
using query_intermediate::query_intermediate;
|
||||
@@ -96,36 +96,36 @@ private:
|
||||
|
||||
class query_offset_intermediate;
|
||||
|
||||
class query_limit_intermediate : public query_select_finish
|
||||
class query_limit_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_offset_intermediate offset(size_t offset);
|
||||
};
|
||||
|
||||
class query_offset_intermediate : public query_select_finish
|
||||
class query_offset_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_limit_intermediate limit(size_t limit);
|
||||
};
|
||||
|
||||
class query_order_direction_intermediate : public query_select_finish
|
||||
class query_order_direction_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_limit_intermediate limit(size_t limit);
|
||||
};
|
||||
|
||||
class query_order_by_intermediate;
|
||||
|
||||
class query_group_by_intermediate : public query_select_finish
|
||||
class query_group_by_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_order_by_intermediate order_by(const column &col);
|
||||
};
|
||||
@@ -139,10 +139,10 @@ public:
|
||||
query_order_direction_intermediate desc();
|
||||
};
|
||||
|
||||
class query_where_intermediate : public query_select_finish
|
||||
class query_where_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_group_by_intermediate group_by(const column &col);
|
||||
query_order_by_intermediate order_by(const column &col);
|
||||
@@ -150,10 +150,10 @@ public:
|
||||
|
||||
class query_join_intermediate;
|
||||
|
||||
class query_on_intermediate : public query_select_finish
|
||||
class query_on_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_join_intermediate join_left(const table &t);
|
||||
template<class Condition>
|
||||
@@ -191,10 +191,10 @@ private:
|
||||
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
|
||||
};
|
||||
|
||||
class query_from_intermediate : public query_select_finish
|
||||
class query_from_intermediate : public query_select
|
||||
{
|
||||
public:
|
||||
using query_select_finish::query_select_finish;
|
||||
using query_select::query_select;
|
||||
|
||||
query_join_intermediate join_left(const table &t);
|
||||
template<class Condition>
|
||||
@@ -244,16 +244,16 @@ class query_into_intermediate : public query_intermediate
|
||||
public:
|
||||
using query_intermediate::query_intermediate;
|
||||
|
||||
query_execute_finish values(std::initializer_list<any_type> values);
|
||||
query_execute_finish values(std::vector<any_type> &&values);
|
||||
query_execute values(std::initializer_list<any_type> values);
|
||||
query_execute values(std::vector<any_type> &&values);
|
||||
template<class Type>
|
||||
query_execute_finish values()
|
||||
query_execute values()
|
||||
{
|
||||
Type obj;
|
||||
return values(std::move(as_placeholder(obj)));
|
||||
}
|
||||
template<class Type>
|
||||
query_execute_finish values(const Type &obj)
|
||||
query_execute values(const Type &obj)
|
||||
{
|
||||
return values(std::move(value_extractor::extract(obj)));
|
||||
}
|
||||
@@ -264,10 +264,10 @@ class query_create_intermediate : public query_start_intermediate
|
||||
public:
|
||||
explicit query_create_intermediate(connection &db, const sql::schema &schema);
|
||||
|
||||
query_execute_finish table(const sql::table &table, std::initializer_list<column_definition> columns);
|
||||
query_execute_finish table(const sql::table &table, const std::vector<column_definition> &columns);
|
||||
query_execute table(const sql::table &table, std::initializer_list<column_definition> columns);
|
||||
query_execute table(const sql::table &table, const std::vector<column_definition> &columns);
|
||||
template<class Type>
|
||||
query_execute_finish table(const sql::table &table)
|
||||
query_execute table(const sql::table &table)
|
||||
{
|
||||
return this->table(table, column_definition_generator::generate<Type>(schema_));
|
||||
}
|
||||
@@ -278,7 +278,7 @@ class query_drop_intermediate : query_start_intermediate
|
||||
public:
|
||||
explicit query_drop_intermediate(connection &db, const sql::schema &schema);
|
||||
|
||||
query_execute_finish table(const sql::table &table);
|
||||
query_execute table(const sql::table &table);
|
||||
};
|
||||
|
||||
class query_insert_intermediate : public query_start_intermediate
|
||||
@@ -291,18 +291,18 @@ public:
|
||||
query_into_intermediate into(const sql::table &table);
|
||||
};
|
||||
|
||||
class query_execute_where_intermediate : public query_execute_finish
|
||||
class query_execute_where_intermediate : public query_execute
|
||||
{
|
||||
public:
|
||||
using query_execute_finish::query_execute_finish;
|
||||
using query_execute::query_execute;
|
||||
|
||||
query_order_by_intermediate order_by(const column &col);
|
||||
};
|
||||
|
||||
class query_set_intermediate : public query_execute_finish
|
||||
class query_set_intermediate : public query_execute
|
||||
{
|
||||
public:
|
||||
using query_execute_finish::query_execute_finish;
|
||||
using query_execute::query_execute;
|
||||
|
||||
template<class Condition>
|
||||
query_execute_where_intermediate where(const Condition &cond)
|
||||
@@ -328,10 +328,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class query_delete_from_intermediate : public query_execute_finish
|
||||
class query_delete_from_intermediate : public query_execute
|
||||
{
|
||||
public:
|
||||
using query_execute_finish::query_execute_finish;
|
||||
using query_execute::query_execute;
|
||||
|
||||
template<class Condition>
|
||||
query_execute_where_intermediate where(const Condition &cond)
|
||||
|
||||
@@ -14,6 +14,14 @@ namespace matador::sql {
|
||||
|
||||
class dialect;
|
||||
|
||||
enum class session_error {
|
||||
Ok = 0,
|
||||
NoConnectionAvailable,
|
||||
UnknownType,
|
||||
FailedToBuildQuery,
|
||||
FailedToFindObject
|
||||
};
|
||||
|
||||
class session
|
||||
{
|
||||
public:
|
||||
@@ -33,35 +41,68 @@ public:
|
||||
}
|
||||
|
||||
template<typename Type, typename PrimaryKeyType>
|
||||
std::optional<entity<Type>> find(const PrimaryKeyType &pk) {
|
||||
utils::result<entity<Type>, session_error> find(const PrimaryKeyType &pk) {
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
throw std::logic_error("no database connection available");
|
||||
return utils::error(session_error::NoConnectionAvailable);
|
||||
}
|
||||
auto info = schema_->info<Type>();
|
||||
if (!info) {
|
||||
return {};
|
||||
return utils::error(session_error::UnknownType);
|
||||
}
|
||||
|
||||
entity_query_builder eqb(*schema_);
|
||||
auto data = eqb.build<Type>(pk);
|
||||
|
||||
auto q = c->query(*schema_)
|
||||
.select(data->columns)
|
||||
.from(data->root_table_name);
|
||||
|
||||
for (auto &jd : data->joins) {
|
||||
q.join_left(jd.join_table)
|
||||
.on(std::move(jd.condition));
|
||||
if (!data.is_ok()) {
|
||||
return utils::error(session_error::FailedToBuildQuery);
|
||||
}
|
||||
auto e = q
|
||||
.where(std::move(data->where_clause))
|
||||
.template fetch_one<Type>();
|
||||
|
||||
if (!e) {
|
||||
return std::nullopt;
|
||||
auto obj = build_select_query(c, data.release()).template fetch_one<Type>();
|
||||
|
||||
if (!obj) {
|
||||
return utils::error(session_error::FailedToFindObject);
|
||||
}
|
||||
return entity<Type>{ e.release() };
|
||||
return utils::ok(entity<Type>{ obj.release() });
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
utils::result<query_result<Type>, session_error> find() {
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
return utils::error(session_error::NoConnectionAvailable);
|
||||
}
|
||||
auto info = schema_->info<Type>();
|
||||
if (!info) {
|
||||
return utils::error(session_error::UnknownType);
|
||||
}
|
||||
|
||||
entity_query_builder eqb(*schema_);
|
||||
auto data = eqb.build<Type>();
|
||||
if (!data.is_ok()) {
|
||||
return utils::error(session_error::FailedToBuildQuery);
|
||||
}
|
||||
|
||||
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
utils::result<query_from_intermediate, session_error> select() {
|
||||
auto c = pool_.acquire();
|
||||
if (!c.valid()) {
|
||||
return utils::error(session_error::NoConnectionAvailable);
|
||||
}
|
||||
auto info = schema_->info<Type>();
|
||||
if (!info) {
|
||||
return utils::error(session_error::UnknownType);
|
||||
}
|
||||
|
||||
entity_query_builder eqb(*schema_);
|
||||
auto data = eqb.build<Type>();
|
||||
if (!data.is_ok()) {
|
||||
return utils::error(session_error::FailedToBuildQuery);
|
||||
}
|
||||
|
||||
return utils::ok(build_select_query(c, data.release()).template fetch_all<Type>());
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
@@ -79,10 +120,12 @@ public:
|
||||
const class dialect& dialect() const;
|
||||
|
||||
private:
|
||||
friend class query_select_finish;
|
||||
friend class query_select;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
||||
|
||||
query_select build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const;
|
||||
|
||||
private:
|
||||
connection_pool<connection> &pool_;
|
||||
const class dialect &dialect_;
|
||||
|
||||
@@ -18,11 +18,13 @@ template < typename ValueType >
|
||||
class ok
|
||||
{
|
||||
public:
|
||||
using value_type = ValueType;
|
||||
explicit constexpr ok(const ValueType &value) : value_(value) {}
|
||||
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
|
||||
|
||||
constexpr ValueType&& release() { return std::move(value_); }
|
||||
const ValueType& value() const { return value_; }
|
||||
ValueType& value() { return value_; }
|
||||
|
||||
private:
|
||||
ValueType value_;
|
||||
@@ -32,11 +34,14 @@ template < typename ErrorType >
|
||||
class error
|
||||
{
|
||||
public:
|
||||
using value_type = ErrorType;
|
||||
|
||||
explicit constexpr error(const ErrorType &error) : error_(error) {}
|
||||
explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {}
|
||||
|
||||
constexpr ErrorType&& release() { return std::move(error_); }
|
||||
const ErrorType& value() const { return error_; }
|
||||
ErrorType value() { return error_; }
|
||||
|
||||
private:
|
||||
ErrorType error_;
|
||||
@@ -49,8 +54,13 @@ public:
|
||||
using value_type = ok<ValueType>;
|
||||
using error_type = error<ErrorType>;
|
||||
|
||||
result(value_type value) : result_(value) {} // NOLINT(*-explicit-constructor)
|
||||
result(error_type error) : result_(error) {} // NOLINT(*-explicit-constructor)
|
||||
result() : result_(ValueType{}) {}
|
||||
result(value_type value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor)
|
||||
result(error_type error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor)
|
||||
result(const result<ValueType, ErrorType> &x) = default;
|
||||
result& operator=(const result<ValueType, ErrorType> &x) = default;
|
||||
result(result<ValueType, ErrorType> &&x) = default;
|
||||
result& operator=(result<ValueType, ErrorType> &&x) = default;
|
||||
|
||||
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
|
||||
|
||||
@@ -65,15 +75,33 @@ public:
|
||||
ErrorType err() { return std::get<error_type>(result_).value(); }
|
||||
|
||||
constexpr const ValueType* operator->() const { return &value(); }
|
||||
constexpr ValueType* operator->() { return &value(); }
|
||||
constexpr ValueType* operator->() { return &std::get<value_type>(result_).value(); }
|
||||
|
||||
template<typename SecondValueType, typename SecondErrorType, typename Func>
|
||||
result<SecondValueType, SecondErrorType> and_then(Func f) {
|
||||
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >>
|
||||
result<SecondValueType, ErrorType> transform(Func &&f) {
|
||||
if (is_ok()) {
|
||||
return f(value());
|
||||
return result<SecondValueType, ErrorType>(ok(f(release())));
|
||||
}
|
||||
|
||||
return *this;
|
||||
return result<SecondValueType, ErrorType>(error(release_error()));
|
||||
}
|
||||
|
||||
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >::value_type::value_type>
|
||||
result<SecondValueType, ErrorType> and_then(Func &&f) {
|
||||
if (is_ok()) {
|
||||
return f(release());
|
||||
}
|
||||
|
||||
return result<SecondValueType, ErrorType>(error(release_error()));
|
||||
}
|
||||
|
||||
template<typename Func, typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::error_type::value_type>
|
||||
result<ValueType, SecondErrorType> or_else(Func &&f) {
|
||||
if (is_error()) {
|
||||
return f(err());
|
||||
}
|
||||
|
||||
return result<ValueType, SecondErrorType>(ok(release()));
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Reference in New Issue
Block a user