use result in entity_query_builder
This commit is contained in:
parent
b7c12d8217
commit
25bcc362f2
|
|
@ -51,9 +51,46 @@ TEST_CASE_METHOD(SessionFixture, "Use session to find object with id", "[session
|
||||||
ses.create_schema();
|
ses.create_schema();
|
||||||
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
|
auto a380 = ses.insert<airplane>(1, "Boeing", "A380");
|
||||||
|
|
||||||
auto result = ses.find<airplane>(1);
|
auto result = ses.find<airplane>(2);
|
||||||
|
REQUIRE(!result.is_ok());
|
||||||
|
REQUIRE((result.err() == sql::session_error::FailedToFindObject));
|
||||||
|
|
||||||
|
result = ses.find<airplane>(1);
|
||||||
|
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
auto read_a380 = result.value();
|
auto read_a380 = result.value();
|
||||||
REQUIRE(a380->id == read_a380->id);
|
REQUIRE(a380->id == read_a380->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(SessionFixture, "Use session to find all objects", "[session][find]") {
|
||||||
|
using namespace matador::test;
|
||||||
|
ses.attach<airplane>("airplane");
|
||||||
|
ses.create_schema();
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<airplane>> planes;
|
||||||
|
planes.emplace_back(new airplane(1, "Airbus", "A380"));
|
||||||
|
planes.emplace_back(new airplane(2, "Boeing", "707"));
|
||||||
|
planes.emplace_back(new airplane(3, "Boeing", "747"));
|
||||||
|
|
||||||
|
for (auto &&plane: planes) {
|
||||||
|
ses.insert(plane.release());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = ses.find<airplane>();
|
||||||
|
|
||||||
|
std::vector<std::tuple<int, std::string, std::string>> expected_result {
|
||||||
|
{1, "Airbus", "A380"},
|
||||||
|
{2, "Boeing", "707"},
|
||||||
|
{3, "Boeing", "747"}
|
||||||
|
};
|
||||||
|
REQUIRE(result);
|
||||||
|
auto all_planes = result.release();
|
||||||
|
size_t index {0};
|
||||||
|
for (const auto &i: all_planes) {
|
||||||
|
REQUIRE(i.id == std::get<0>(expected_result[index]));
|
||||||
|
REQUIRE(i.brand == std::get<1>(expected_result[index]));
|
||||||
|
REQUIRE(i.model == std::get<2>(expected_result[index]));
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
#include "matador/sql/query_intermediates.hpp"
|
#include "matador/sql/query_intermediates.hpp"
|
||||||
#include "matador/sql/value.hpp"
|
#include "matador/sql/value.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
@ -25,6 +27,24 @@ struct entity_query_data {
|
||||||
std::unique_ptr<basic_condition> where_clause;
|
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
|
class entity_query_builder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -32,18 +52,45 @@ public:
|
||||||
: schema_(scm) {}
|
: schema_(scm) {}
|
||||||
|
|
||||||
template<class EntityType, typename PrimaryKeyType>
|
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>();
|
const auto info = schema_.info<EntityType>();
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return std::nullopt;
|
return utils::error(query_build_error::UnknownType);
|
||||||
}
|
}
|
||||||
pk_ = pk;
|
pk_ = pk;
|
||||||
table_info_stack_.push(info.value());
|
table_info_stack_.push(info.value());
|
||||||
entity_query_data_ = { info->name };
|
entity_query_data_ = { info->name };
|
||||||
EntityType obj;
|
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 >
|
template < class V >
|
||||||
|
|
@ -65,51 +112,15 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
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) {
|
on_foreign_object(id, obj, attr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Pointer>
|
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) {
|
on_foreign_object(id, obj, attr);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
|
|
@ -118,7 +129,7 @@ public:
|
||||||
if (attr.fetch() == utils::fetch_type::EAGER) {
|
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return;
|
throw query_builder_exception{query_build_error::UnknownType};
|
||||||
}
|
}
|
||||||
table_info_stack_.push(info.value());
|
table_info_stack_.push(info.value());
|
||||||
typename ContainerType::value_type::value_type obj;
|
typename ContainerType::value_type::value_type obj;
|
||||||
|
|
@ -127,8 +138,7 @@ public:
|
||||||
|
|
||||||
auto pk = info->prototype.primary_key();
|
auto pk = info->prototype.primary_key();
|
||||||
if (!pk) {
|
if (!pk) {
|
||||||
// error, prototype doesn't has a pk
|
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {info->name, join_column});
|
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) {
|
if (attr.fetch() == utils::fetch_type::EAGER) {
|
||||||
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
const auto info = schema_.info<typename ContainerType::value_type::value_type>();
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return;
|
throw query_builder_exception{query_build_error::UnknownType};
|
||||||
}
|
}
|
||||||
table_info_stack_.push(info.value());
|
table_info_stack_.push(info.value());
|
||||||
typename ContainerType::value_type::value_type obj;
|
typename ContainerType::value_type::value_type obj;
|
||||||
|
|
@ -150,8 +160,7 @@ public:
|
||||||
|
|
||||||
auto pk = info->prototype.primary_key();
|
auto pk = info->prototype.primary_key();
|
||||||
if (!pk) {
|
if (!pk) {
|
||||||
// error, prototype doesn't has a pk
|
throw query_builder_exception{query_build_error::MissingPrimaryKey};
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
append_join({table_info_stack_.top().name, table_info_stack_.top().prototype.primary_key()->name()}, {id, join_column});
|
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*/) {}
|
void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
template<class Pointer>
|
||||||
|
void on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr);
|
||||||
void push(const std::string &column_name);
|
void push(const std::string &column_name);
|
||||||
[[nodiscard]] bool is_root_entity() const;
|
[[nodiscard]] bool is_root_entity() const;
|
||||||
void append_join(const column &left, const column &right);
|
void append_join(const column &left, const column &right);
|
||||||
|
|
@ -175,5 +186,28 @@ private:
|
||||||
int column_index{0};
|
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
|
#endif //QUERY_ENTITY_QUERY_BUILDER_HPP
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ protected:
|
||||||
std::shared_ptr<query_data> data_;
|
std::shared_ptr<query_data> data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_execute_finish : public query_intermediate
|
class query_execute : public query_intermediate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
@ -50,7 +50,7 @@ public:
|
||||||
[[nodiscard]] query_context build() const;
|
[[nodiscard]] query_context build() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_select_finish : public query_intermediate
|
class query_select : public query_intermediate
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
@ -96,36 +96,36 @@ private:
|
||||||
|
|
||||||
class query_offset_intermediate;
|
class query_offset_intermediate;
|
||||||
|
|
||||||
class query_limit_intermediate : public query_select_finish
|
class query_limit_intermediate : public query_select
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_offset_intermediate offset(size_t offset);
|
query_offset_intermediate offset(size_t offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_offset_intermediate : public query_select_finish
|
class query_offset_intermediate : public query_select
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_limit_intermediate limit(size_t limit);
|
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:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_limit_intermediate limit(size_t limit);
|
query_limit_intermediate limit(size_t limit);
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_order_by_intermediate;
|
class query_order_by_intermediate;
|
||||||
|
|
||||||
class query_group_by_intermediate : public query_select_finish
|
class query_group_by_intermediate : public query_select
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_order_by_intermediate order_by(const column &col);
|
query_order_by_intermediate order_by(const column &col);
|
||||||
};
|
};
|
||||||
|
|
@ -139,10 +139,10 @@ public:
|
||||||
query_order_direction_intermediate desc();
|
query_order_direction_intermediate desc();
|
||||||
};
|
};
|
||||||
|
|
||||||
class query_where_intermediate : public query_select_finish
|
class query_where_intermediate : public query_select
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_group_by_intermediate group_by(const column &col);
|
query_group_by_intermediate group_by(const column &col);
|
||||||
query_order_by_intermediate order_by(const column &col);
|
query_order_by_intermediate order_by(const column &col);
|
||||||
|
|
@ -150,10 +150,10 @@ public:
|
||||||
|
|
||||||
class query_join_intermediate;
|
class query_join_intermediate;
|
||||||
|
|
||||||
class query_on_intermediate : public query_select_finish
|
class query_on_intermediate : public query_select
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_join_intermediate join_left(const table &t);
|
query_join_intermediate join_left(const table &t);
|
||||||
template<class Condition>
|
template<class Condition>
|
||||||
|
|
@ -191,10 +191,10 @@ private:
|
||||||
query_on_intermediate on_clause(std::unique_ptr<basic_condition> &&cond);
|
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:
|
public:
|
||||||
using query_select_finish::query_select_finish;
|
using query_select::query_select;
|
||||||
|
|
||||||
query_join_intermediate join_left(const table &t);
|
query_join_intermediate join_left(const table &t);
|
||||||
template<class Condition>
|
template<class Condition>
|
||||||
|
|
@ -244,16 +244,16 @@ class query_into_intermediate : public query_intermediate
|
||||||
public:
|
public:
|
||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
||||||
query_execute_finish values(std::initializer_list<any_type> values);
|
query_execute values(std::initializer_list<any_type> values);
|
||||||
query_execute_finish values(std::vector<any_type> &&values);
|
query_execute values(std::vector<any_type> &&values);
|
||||||
template<class Type>
|
template<class Type>
|
||||||
query_execute_finish values()
|
query_execute values()
|
||||||
{
|
{
|
||||||
Type obj;
|
Type obj;
|
||||||
return values(std::move(as_placeholder(obj)));
|
return values(std::move(as_placeholder(obj)));
|
||||||
}
|
}
|
||||||
template<class Type>
|
template<class Type>
|
||||||
query_execute_finish values(const Type &obj)
|
query_execute values(const Type &obj)
|
||||||
{
|
{
|
||||||
return values(std::move(value_extractor::extract(obj)));
|
return values(std::move(value_extractor::extract(obj)));
|
||||||
}
|
}
|
||||||
|
|
@ -264,10 +264,10 @@ class query_create_intermediate : public query_start_intermediate
|
||||||
public:
|
public:
|
||||||
explicit query_create_intermediate(connection &db, const sql::schema &schema);
|
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 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, const std::vector<column_definition> &columns);
|
||||||
template<class Type>
|
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_));
|
return this->table(table, column_definition_generator::generate<Type>(schema_));
|
||||||
}
|
}
|
||||||
|
|
@ -278,7 +278,7 @@ class query_drop_intermediate : query_start_intermediate
|
||||||
public:
|
public:
|
||||||
explicit query_drop_intermediate(connection &db, const sql::schema &schema);
|
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
|
class query_insert_intermediate : public query_start_intermediate
|
||||||
|
|
@ -291,18 +291,18 @@ public:
|
||||||
query_into_intermediate into(const sql::table &table);
|
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:
|
public:
|
||||||
using query_execute_finish::query_execute_finish;
|
using query_execute::query_execute;
|
||||||
|
|
||||||
query_order_by_intermediate order_by(const column &col);
|
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:
|
public:
|
||||||
using query_execute_finish::query_execute_finish;
|
using query_execute::query_execute;
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition>
|
||||||
query_execute_where_intermediate where(const Condition &cond)
|
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:
|
public:
|
||||||
using query_execute_finish::query_execute_finish;
|
using query_execute::query_execute;
|
||||||
|
|
||||||
template<class Condition>
|
template<class Condition>
|
||||||
query_execute_where_intermediate where(const Condition &cond)
|
query_execute_where_intermediate where(const Condition &cond)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,14 @@ namespace matador::sql {
|
||||||
|
|
||||||
class dialect;
|
class dialect;
|
||||||
|
|
||||||
|
enum class session_error {
|
||||||
|
Ok = 0,
|
||||||
|
NoConnectionAvailable,
|
||||||
|
UnknownType,
|
||||||
|
FailedToBuildQuery,
|
||||||
|
FailedToFindObject
|
||||||
|
};
|
||||||
|
|
||||||
class session
|
class session
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -33,35 +41,68 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type, typename PrimaryKeyType>
|
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();
|
auto c = pool_.acquire();
|
||||||
if (!c.valid()) {
|
if (!c.valid()) {
|
||||||
throw std::logic_error("no database connection available");
|
return utils::error(session_error::NoConnectionAvailable);
|
||||||
}
|
}
|
||||||
auto info = schema_->info<Type>();
|
auto info = schema_->info<Type>();
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return {};
|
return utils::error(session_error::UnknownType);
|
||||||
}
|
}
|
||||||
|
|
||||||
entity_query_builder eqb(*schema_);
|
entity_query_builder eqb(*schema_);
|
||||||
auto data = eqb.build<Type>(pk);
|
auto data = eqb.build<Type>(pk);
|
||||||
|
if (!data.is_ok()) {
|
||||||
auto q = c->query(*schema_)
|
return utils::error(session_error::FailedToBuildQuery);
|
||||||
.select(data->columns)
|
|
||||||
.from(data->root_table_name);
|
|
||||||
|
|
||||||
for (auto &jd : data->joins) {
|
|
||||||
q.join_left(jd.join_table)
|
|
||||||
.on(std::move(jd.condition));
|
|
||||||
}
|
}
|
||||||
auto e = q
|
|
||||||
.where(std::move(data->where_clause))
|
|
||||||
.template fetch_one<Type>();
|
|
||||||
|
|
||||||
if (!e) {
|
auto obj = build_select_query(c, data.release()).template fetch_one<Type>();
|
||||||
return std::nullopt;
|
|
||||||
|
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>
|
template<typename Type>
|
||||||
|
|
@ -79,10 +120,12 @@ public:
|
||||||
const class dialect& dialect() const;
|
const class dialect& dialect() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class query_select_finish;
|
friend class query_select;
|
||||||
|
|
||||||
[[nodiscard]] std::unique_ptr<query_result_impl> fetch(const std::string &sql) const;
|
[[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:
|
private:
|
||||||
connection_pool<connection> &pool_;
|
connection_pool<connection> &pool_;
|
||||||
const class dialect &dialect_;
|
const class dialect &dialect_;
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,13 @@ template < typename ValueType >
|
||||||
class ok
|
class ok
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using value_type = ValueType;
|
||||||
explicit constexpr ok(const ValueType &value) : value_(value) {}
|
explicit constexpr ok(const ValueType &value) : value_(value) {}
|
||||||
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
|
explicit constexpr ok(ValueType &&value) : value_(std::move(value)) {}
|
||||||
|
|
||||||
constexpr ValueType&& release() { return std::move(value_); }
|
constexpr ValueType&& release() { return std::move(value_); }
|
||||||
const ValueType& value() const { return value_; }
|
const ValueType& value() const { return value_; }
|
||||||
|
ValueType& value() { return value_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ValueType value_;
|
ValueType value_;
|
||||||
|
|
@ -32,11 +34,14 @@ template < typename ErrorType >
|
||||||
class error
|
class error
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
using value_type = ErrorType;
|
||||||
|
|
||||||
explicit constexpr error(const ErrorType &error) : error_(error) {}
|
explicit constexpr error(const ErrorType &error) : error_(error) {}
|
||||||
explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {}
|
explicit constexpr error(ErrorType &&error) : error_(std::move(error)) {}
|
||||||
|
|
||||||
constexpr ErrorType&& release() { return std::move(error_); }
|
constexpr ErrorType&& release() { return std::move(error_); }
|
||||||
const ErrorType& value() const { return error_; }
|
const ErrorType& value() const { return error_; }
|
||||||
|
ErrorType value() { return error_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorType error_;
|
ErrorType error_;
|
||||||
|
|
@ -49,8 +54,13 @@ public:
|
||||||
using value_type = ok<ValueType>;
|
using value_type = ok<ValueType>;
|
||||||
using error_type = error<ErrorType>;
|
using error_type = error<ErrorType>;
|
||||||
|
|
||||||
result(value_type value) : result_(value) {} // NOLINT(*-explicit-constructor)
|
result() : result_(ValueType{}) {}
|
||||||
result(error_type error) : result_(error) {} // NOLINT(*-explicit-constructor)
|
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)
|
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
|
||||||
|
|
||||||
|
|
@ -65,15 +75,33 @@ public:
|
||||||
ErrorType err() { return std::get<error_type>(result_).value(); }
|
ErrorType err() { return std::get<error_type>(result_).value(); }
|
||||||
|
|
||||||
constexpr const ValueType* operator->() const { return &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>
|
template<typename Func, typename SecondValueType = typename std::invoke_result_t<Func, ValueType >>
|
||||||
result<SecondValueType, SecondErrorType> and_then(Func f) {
|
result<SecondValueType, ErrorType> transform(Func &&f) {
|
||||||
if (is_ok()) {
|
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:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ basic_query_intermediate::basic_query_intermediate(connection &db, const sql::sc
|
||||||
: connection_(db)
|
: connection_(db)
|
||||||
, schema_(schema) {}
|
, schema_(schema) {}
|
||||||
|
|
||||||
query_result<record> query_select_finish::fetch_all()
|
query_result<record> query_select::fetch_all()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return connection_.fetch(compiler.compile(data_.get()));
|
return connection_.fetch(compiler.compile(data_.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<record> query_select_finish::fetch_one()
|
std::optional<record> query_select::fetch_one()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
auto result = connection_.fetch(compiler.compile(data_.get()));
|
auto result = connection_.fetch(compiler.compile(data_.get()));
|
||||||
|
|
@ -26,19 +26,19 @@ std::optional<record> query_select_finish::fetch_one()
|
||||||
return *first.get();
|
return *first.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
query_context query_select_finish::build() const
|
query_context query_select::build() const
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return compiler.compile(data_.get());
|
return compiler.compile(data_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<query_result_impl> query_select_finish::fetch()
|
std::unique_ptr<query_result_impl> query_select::fetch()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return connection_.fetch(compiler.compile(data_.get()).sql);
|
return connection_.fetch(compiler.compile(data_.get()).sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement query_select_finish::prepare()
|
statement query_select::prepare()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return connection_.prepare(compiler.compile(data_.get()));
|
return connection_.prepare(compiler.compile(data_.get()));
|
||||||
|
|
@ -184,30 +184,30 @@ query_into_intermediate query_insert_intermediate::into(const table &table)
|
||||||
return {connection_, schema_, data_};
|
return {connection_, schema_, data_};
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t query_execute_finish::execute()
|
size_t query_execute::execute()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return connection_.execute(compiler.compile(data_.get()).sql);
|
return connection_.execute(compiler.compile(data_.get()).sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
statement query_execute_finish::prepare()
|
statement query_execute::prepare()
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return connection_.prepare(compiler.compile(data_.get()));
|
return connection_.prepare(compiler.compile(data_.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
query_context query_execute_finish::build() const
|
query_context query_execute::build() const
|
||||||
{
|
{
|
||||||
query_compiler compiler(connection_.dialect());
|
query_compiler compiler(connection_.dialect());
|
||||||
return compiler.compile(data_.get());
|
return compiler.compile(data_.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
query_execute_finish query_into_intermediate::values(std::initializer_list<any_type> values)
|
query_execute query_into_intermediate::values(std::initializer_list<any_type> values)
|
||||||
{
|
{
|
||||||
return this->values(std::vector<any_type>(values));
|
return this->values(std::vector<any_type>(values));
|
||||||
}
|
}
|
||||||
|
|
||||||
query_execute_finish query_into_intermediate::values(std::vector<any_type> &&values)
|
query_execute query_into_intermediate::values(std::vector<any_type> &&values)
|
||||||
{
|
{
|
||||||
data_->parts.push_back(std::make_unique<query_values_part>(std::move(values)));
|
data_->parts.push_back(std::make_unique<query_values_part>(std::move(values)));
|
||||||
return {connection_, schema_, data_};
|
return {connection_, schema_, data_};
|
||||||
|
|
@ -218,12 +218,12 @@ query_create_intermediate::query_create_intermediate(connection &db, const sql::
|
||||||
data_->parts.push_back(std::make_unique<query_create_part>());
|
data_->parts.push_back(std::make_unique<query_create_part>());
|
||||||
}
|
}
|
||||||
|
|
||||||
query_execute_finish query_create_intermediate::table(const sql::table &table, std::initializer_list<column_definition> columns)
|
query_execute query_create_intermediate::table(const sql::table &table, std::initializer_list<column_definition> columns)
|
||||||
{
|
{
|
||||||
return this->table(table, std::vector<column_definition>{columns});
|
return this->table(table, std::vector<column_definition>{columns});
|
||||||
}
|
}
|
||||||
|
|
||||||
query_execute_finish query_create_intermediate::table(const sql::table &table, const std::vector<column_definition> &columns)
|
query_execute query_create_intermediate::table(const sql::table &table, const std::vector<column_definition> &columns)
|
||||||
{
|
{
|
||||||
data_->parts.push_back(std::make_unique<query_create_table_part>(table, columns));
|
data_->parts.push_back(std::make_unique<query_create_table_part>(table, columns));
|
||||||
return {connection_, schema_, data_};
|
return {connection_, schema_, data_};
|
||||||
|
|
@ -235,7 +235,7 @@ query_drop_intermediate::query_drop_intermediate(connection &db, const sql::sche
|
||||||
data_->parts.push_back(std::make_unique<query_drop_part>());
|
data_->parts.push_back(std::make_unique<query_drop_part>());
|
||||||
}
|
}
|
||||||
|
|
||||||
query_execute_finish query_drop_intermediate::table(const sql::table &table)
|
query_execute query_drop_intermediate::table(const sql::table &table)
|
||||||
{
|
{
|
||||||
data_->parts.push_back(std::make_unique<query_drop_table_part>(table));
|
data_->parts.push_back(std::make_unique<query_drop_table_part>(table));
|
||||||
return {connection_, schema_, data_};
|
return {connection_, schema_, data_};
|
||||||
|
|
|
||||||
|
|
@ -103,4 +103,26 @@ std::unique_ptr<query_result_impl> session::fetch(const std::string &sql) const
|
||||||
return c->fetch(sql);
|
return c->fetch(sql);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
query_select session::build_select_query(connection_ptr<connection> &conn, entity_query_data &&data) const
|
||||||
|
{
|
||||||
|
auto q = conn->query(*schema_)
|
||||||
|
.select(data.columns)
|
||||||
|
.from(data.root_table_name);
|
||||||
|
|
||||||
|
auto it = data.joins.begin();
|
||||||
|
if (it != data.joins.end()) {
|
||||||
|
auto qj = q.join_left(it->join_table).on(std::move(it->condition));
|
||||||
|
while (++it != data.joins.end()) {
|
||||||
|
qj.join_left(it->join_table).on(std::move(it->condition));
|
||||||
|
}
|
||||||
|
if (data.where_clause) {
|
||||||
|
return qj.where(std::move(data.where_clause));
|
||||||
|
}
|
||||||
|
return qj;
|
||||||
|
} else if (data.where_clause) {
|
||||||
|
return q.where(std::move(data.where_clause));
|
||||||
|
} else {
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent
|
||||||
|
|
||||||
auto data = eqb.build<book>(17);
|
auto data = eqb.build<book>(17);
|
||||||
|
|
||||||
REQUIRE(data.has_value());
|
REQUIRE(data.is_ok());
|
||||||
REQUIRE(data->root_table_name == "books");
|
REQUIRE(data->root_table_name == "books");
|
||||||
REQUIRE(data->joins.size() == 1);
|
REQUIRE(data->joins.size() == 1);
|
||||||
const std::vector<column> expected_columns {
|
const std::vector<column> expected_columns {
|
||||||
|
|
@ -81,7 +81,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q
|
||||||
|
|
||||||
auto data = eqb.build<order>(17);
|
auto data = eqb.build<order>(17);
|
||||||
|
|
||||||
REQUIRE(data.has_value());
|
REQUIRE(data.is_ok());
|
||||||
REQUIRE(data->root_table_name == "orders");
|
REQUIRE(data->root_table_name == "orders");
|
||||||
REQUIRE(data->joins.size() == 1);
|
REQUIRE(data->joins.size() == 1);
|
||||||
const std::vector<column> expected_columns = {
|
const std::vector<column> expected_columns = {
|
||||||
|
|
@ -135,7 +135,7 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e
|
||||||
|
|
||||||
auto data = eqb.build<ingredient>(17);
|
auto data = eqb.build<ingredient>(17);
|
||||||
|
|
||||||
REQUIRE(data.has_value());
|
REQUIRE(data.is_ok());
|
||||||
REQUIRE(data->root_table_name == "ingredients");
|
REQUIRE(data->root_table_name == "ingredients");
|
||||||
REQUIRE(data->joins.size() == 2);
|
REQUIRE(data->joins.size() == 2);
|
||||||
const std::vector<column> expected_columns {
|
const std::vector<column> expected_columns {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,14 @@ utils::result<float, math_error>multiply(int x, int y) {
|
||||||
return utils::ok(float(x) * y);
|
return utils::ok(float(x) * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<float, math_error>plus(int x, int y) {
|
||||||
|
return utils::ok(float(x) + y);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<float, std::string>error_to_string(math_error err) {
|
||||||
|
return utils::error(std::string("error"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace matador;
|
using namespace matador;
|
||||||
|
|
@ -41,7 +49,42 @@ TEST_CASE("Result tests", "[result]") {
|
||||||
|
|
||||||
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||||
|
|
||||||
|
res = test::divide(4, 2)
|
||||||
|
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||||
|
.and_then([](const auto &val) { return test::plus(val, 10); });
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(res.is_ok());
|
||||||
|
REQUIRE(!res.is_error());
|
||||||
|
REQUIRE((res.value() == 20.0));
|
||||||
|
|
||||||
|
res = test::divide(4, 0)
|
||||||
|
.and_then([](const auto &val) {
|
||||||
|
return test::multiply(val, 5);
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE(!res);
|
||||||
|
REQUIRE(!res.is_ok());
|
||||||
|
REQUIRE(res.is_error());
|
||||||
|
REQUIRE((res.err() == test::math_error::DIVISION_BY_ZERO));
|
||||||
|
|
||||||
|
auto res2 = test::divide(4, 0)
|
||||||
|
.or_else([](const auto &err) {
|
||||||
|
return test::error_to_string(err);
|
||||||
|
});
|
||||||
|
|
||||||
|
REQUIRE(!res2);
|
||||||
|
REQUIRE(!res2.is_ok());
|
||||||
|
REQUIRE(res2.is_error());
|
||||||
|
REQUIRE((res2.err() == "error"));
|
||||||
|
|
||||||
|
res = test::divide(4, 2)
|
||||||
|
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||||
|
.transform([](const auto &val) { return val + 10; });
|
||||||
|
|
||||||
|
REQUIRE(res);
|
||||||
|
REQUIRE(res.is_ok());
|
||||||
|
REQUIRE(!res.is_error());
|
||||||
|
REQUIRE((res.value() == 20.0));
|
||||||
|
|
||||||
// res = test::divide(4, 2)
|
|
||||||
// .and_then<>()
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue