Compare commits
38
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24677d61df | ||
|
|
5da70ba3a5 | ||
|
|
c880728093 | ||
|
|
78b30f9742 | ||
|
|
b19d0fd3ca | ||
|
|
730ab05213 | ||
|
|
6ecda781f5 | ||
|
|
d4865e4d10 | ||
|
|
06f6166f05 | ||
|
|
89b795c488 | ||
|
|
1b98eb4019 | ||
|
|
47fd6d68e4 | ||
|
|
34a5cfcc88 | ||
|
|
705d361aaa | ||
|
|
87bd2f8b89 | ||
|
|
8914c06833 | ||
|
|
34a9a8aa4a | ||
|
|
12f8590634 | ||
|
|
cea7b97f2b | ||
|
|
bd06034ebf | ||
|
|
68d67b17b7 | ||
|
|
73bd6f641c | ||
|
|
98d58d8e60 | ||
|
|
b22a830d18 | ||
|
|
85e6ee4b93 | ||
|
|
2e1b583741 | ||
|
|
6701031007 | ||
|
|
57f57956de | ||
|
|
141d798a41 | ||
|
|
cc0bcbeb61 | ||
|
|
5ec7c13420 | ||
|
|
ddf19bbf07 | ||
|
|
194e139e8b | ||
|
|
6629a71f6e | ||
|
|
72019bc1e7 | ||
|
|
7216f07c9f | ||
|
|
aa8da1f76f | ||
|
|
627af1d1a8 |
@@ -6,7 +6,10 @@
|
|||||||
|
|
||||||
namespace matador::backends::postgres {
|
namespace matador::backends::postgres {
|
||||||
|
|
||||||
utils::error make_error(const sql::error_code ec, const PGresult *res, const PGconn *db, const std::string &msg,
|
utils::error make_error(const sql::error_code ec,
|
||||||
|
const PGresult *res,
|
||||||
|
const PGconn *db,
|
||||||
|
const std::string &msg,
|
||||||
const std::string &sql) {
|
const std::string &sql) {
|
||||||
utils::error err(ec, msg);
|
utils::error err(ec, msg);
|
||||||
err.add_error_info("dbms", "postgres");
|
err.add_error_info("dbms", "postgres");
|
||||||
@@ -30,22 +33,19 @@ bool is_result_error(const PGresult *res) {
|
|||||||
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
|
return status != PGRES_TUPLES_OK && status != PGRES_COMMAND_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(const char *what, const std::string &source)
|
void throw_postgres_error(const char *what, const std::string &source) {
|
||||||
{
|
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "postgres error (" << source << "): " << what;
|
msg << "postgres error (" << source << "): " << what;
|
||||||
throw std::logic_error(msg.str());
|
throw std::logic_error(msg.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(PGconn *db, const std::string &source)
|
void throw_postgres_error(const PGconn *db, const std::string &source) {
|
||||||
{
|
|
||||||
if (PQstatus(db) == CONNECTION_BAD) {
|
if (PQstatus(db) == CONNECTION_BAD) {
|
||||||
throw_postgres_error(PQerrorMessage(db), source);
|
throw_postgres_error(PQerrorMessage(db), source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void throw_postgres_error(PGresult *res, PGconn *db, const std::string &source, const std::string &sql)
|
void throw_postgres_error(const PGresult *res, const PGconn *db, const std::string &source, const std::string &sql) {
|
||||||
{
|
|
||||||
if (res == nullptr) {
|
if (res == nullptr) {
|
||||||
std::stringstream msg;
|
std::stringstream msg;
|
||||||
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
|
msg << "postgres error (" << source << ", " << PQerrorMessage(db) << ": " << sql;
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ set(TEST_SOURCES
|
|||||||
../../../test/backends/SequenceFixture.cpp
|
../../../test/backends/SequenceFixture.cpp
|
||||||
../../../test/backends/SequenceFixture.hpp
|
../../../test/backends/SequenceFixture.hpp
|
||||||
../../../test/backends/SequenceTest.cpp
|
../../../test/backends/SequenceTest.cpp
|
||||||
|
../../../test/backends/SessionDeleteHasMany.cpp
|
||||||
|
../../../test/backends/SessionDeleteHasManyToMany.cpp
|
||||||
../../../test/backends/SessionFixture.cpp
|
../../../test/backends/SessionFixture.cpp
|
||||||
../../../test/backends/SessionFixture.hpp
|
../../../test/backends/SessionFixture.hpp
|
||||||
../../../test/backends/SessionInsertBelongsTo.cpp
|
../../../test/backends/SessionInsertBelongsTo.cpp
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public:
|
|||||||
[[nodiscard]] const std::string& name() const;
|
[[nodiscard]] const std::string& name() const;
|
||||||
void name(const std::string& n);
|
void name(const std::string& n);
|
||||||
[[nodiscard]] std::string full_name() const;
|
[[nodiscard]] std::string full_name() const;
|
||||||
|
[[nodiscard]] size_t index() const;
|
||||||
[[nodiscard]] const utils::field_attributes& attributes() const;
|
[[nodiscard]] const utils::field_attributes& attributes() const;
|
||||||
[[nodiscard]] utils::field_attributes& attributes();
|
[[nodiscard]] utils::field_attributes& attributes();
|
||||||
[[nodiscard]] bool is_nullable() const;
|
[[nodiscard]] bool is_nullable() const;
|
||||||
@@ -68,6 +69,7 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
|
size_t index_{0};
|
||||||
std::weak_ptr<object> owner_;
|
std::weak_ptr<object> owner_;
|
||||||
utils::basic_type type_{utils::basic_type::Null};
|
utils::basic_type type_{utils::basic_type::Null};
|
||||||
utils::field_attributes options_{};
|
utils::field_attributes options_{};
|
||||||
|
|||||||
@@ -27,12 +27,12 @@ public:
|
|||||||
[[nodiscard]] std::type_index type_index() const;
|
[[nodiscard]] std::type_index type_index() const;
|
||||||
[[nodiscard]] std::string name() const;
|
[[nodiscard]] std::string name() const;
|
||||||
[[nodiscard]] std::shared_ptr<class object> object() const;
|
[[nodiscard]] std::shared_ptr<class object> object() const;
|
||||||
[[nodiscard]] const std::list<attribute>& attributes() const;
|
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||||
[[nodiscard]] const std::list<class restriction>& constraints() const;
|
[[nodiscard]] const std::list<restriction>& constraints() const;
|
||||||
|
|
||||||
[[nodiscard]] bool has_primary_key() const;
|
[[nodiscard]] bool has_primary_key() const;
|
||||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||||
[[nodiscard]] attribute* primary_key_attribute() const;
|
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||||
|
|
||||||
void update_name(const std::string& name) const;
|
void update_name(const std::string& name) const;
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public:
|
|||||||
return basic_info(std::type_index(typeid(Type)));
|
return basic_info(std::type_index(typeid(Type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] utils::result<attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
|
[[nodiscard]] utils::result<const attribute*, utils::error> primary_key_attribute(const std::type_index &ti) const;
|
||||||
|
|
||||||
void dump(std::ostream &os) const;
|
void dump(std::ostream &os) const;
|
||||||
static void dump(std::ostream &os, const repository_node& node);
|
static void dump(std::ostream &os, const repository_node& node);
|
||||||
|
|||||||
@@ -21,31 +21,31 @@ public:
|
|||||||
collection(const collection& other) = default;
|
collection(const collection& other) = default;
|
||||||
|
|
||||||
void push_back(const value_type& value) {
|
void push_back(const value_type& value) {
|
||||||
proxy_->items().push_back(value);
|
proxy_->push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator begin() {
|
iterator begin() {
|
||||||
return proxy_->items().begin();
|
return proxy_->begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator end() {
|
iterator end() {
|
||||||
return proxy_->items().end();
|
return proxy_->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator begin() const {
|
const_iterator begin() const {
|
||||||
return proxy_->items().begin();
|
return proxy_->begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
const_iterator end() const {
|
const_iterator end() const {
|
||||||
return proxy_->items().end();
|
return proxy_->end();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] size_t size() const {
|
[[nodiscard]] size_t size() const {
|
||||||
return proxy_->items().size();
|
return proxy_->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const {
|
[[nodiscard]] bool empty() const {
|
||||||
return proxy_->items().empty();
|
return proxy_->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
|
void reset(std::shared_ptr<collection_proxy<Type>> proxy) {
|
||||||
@@ -53,7 +53,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<collection_proxy<Type>> proxy_;
|
std::shared_ptr<abstract_collection_proxy<Type>> proxy_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
#ifndef MATADOR_COLLECTION_PROXY_HPP
|
#ifndef MATADOR_COLLECTION_PROXY_HPP
|
||||||
#define MATADOR_COLLECTION_PROXY_HPP
|
#define MATADOR_COLLECTION_PROXY_HPP
|
||||||
|
|
||||||
|
|
||||||
#include "matador/object/collection_resolver.hpp"
|
#include "matador/object/collection_resolver.hpp"
|
||||||
|
#include "matador/object/many_to_many_relation.hpp"
|
||||||
|
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/utils/identifier.hpp"
|
||||||
|
|
||||||
@@ -12,13 +12,99 @@
|
|||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
|
// has many primitive
|
||||||
|
// relation<OwnerType, PrimitiveType>
|
||||||
|
// has many ptr
|
||||||
|
// relation<OwnerType, ObjectType>
|
||||||
|
// has many to many
|
||||||
|
// relation<OwnerType, ForeignType>
|
||||||
|
|
||||||
template<typename Type>
|
template < class RelationType >
|
||||||
class collection_proxy final {
|
struct relation_iterator_traits {
|
||||||
|
static RelationType& value(RelationType& item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template < typename Type, typename OwnerType >
|
||||||
|
struct relation_iterator_traits<relation<OwnerType, Type>> {
|
||||||
|
static Type& value(relation<OwnerType, Type>& re) {
|
||||||
|
return re.relation().value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type, typename RelationType = Type>
|
||||||
|
class collection_proxy_iterator {
|
||||||
public:
|
public:
|
||||||
using value_type = Type;
|
using value_type = Type;
|
||||||
using iterator = typename std::vector<value_type>::iterator;
|
using reference = Type&;
|
||||||
using const_iterator = typename std::vector<value_type>::const_iterator;
|
using pointer = Type*;
|
||||||
|
using relation_type = RelationType;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
collection_proxy_iterator() = default;
|
||||||
|
collection_proxy_iterator(typename std::vector<RelationType>::iterator it)
|
||||||
|
: it_(it) {}
|
||||||
|
|
||||||
|
reference operator*() {
|
||||||
|
return relation_iterator_traits<relation_type>::value(*it_);
|
||||||
|
}
|
||||||
|
|
||||||
|
pointer operator->() {
|
||||||
|
return &relation_iterator_traits<relation_type>::value(*it_);
|
||||||
|
}
|
||||||
|
|
||||||
|
collection_proxy_iterator& operator++() {
|
||||||
|
++it_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
bool operator==(const collection_proxy_iterator& other) const {
|
||||||
|
return it_ == other.it_;
|
||||||
|
}
|
||||||
|
bool operator!=(const collection_proxy_iterator& other) const {
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation_type& relation() {
|
||||||
|
|
||||||
|
// return *it_;
|
||||||
|
// }
|
||||||
|
private:
|
||||||
|
typename std::vector<RelationType>::iterator it_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type, typename RelationType = Type>
|
||||||
|
class abstract_collection_proxy {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using relation_type = RelationType;
|
||||||
|
// using iterator = typename std::vector<value_type>::iterator;
|
||||||
|
// using const_iterator = typename std::vector<value_type>::const_iterator;
|
||||||
|
using iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||||
|
using const_iterator = collection_proxy_iterator<value_type, relation_type>;
|
||||||
|
|
||||||
|
virtual ~abstract_collection_proxy() = default;
|
||||||
|
|
||||||
|
virtual void push_back(const value_type& value) = 0;
|
||||||
|
|
||||||
|
virtual iterator begin() = 0;
|
||||||
|
virtual iterator end() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual size_t size() = 0;
|
||||||
|
[[nodiscard]] virtual bool empty() = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual const utils::identifier& owner_id() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::vector<relation_type> items_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
class collection_proxy : public abstract_collection_proxy<Type> {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using iterator = typename abstract_collection_proxy<Type>::iterator;
|
||||||
|
using const_iterator = typename abstract_collection_proxy<Type>::const_iterator;
|
||||||
|
|
||||||
collection_proxy() = default;
|
collection_proxy() = default;
|
||||||
|
|
||||||
@@ -34,17 +120,32 @@ public:
|
|||||||
explicit collection_proxy(std::vector<Type> items)
|
explicit collection_proxy(std::vector<Type> items)
|
||||||
: items_(std::move(items)) {}
|
: items_(std::move(items)) {}
|
||||||
|
|
||||||
[[nodiscard]] const utils::identifier& owner_id() const {
|
[[nodiscard]] const utils::identifier& owner_id() const override {
|
||||||
return owner_id_;
|
return owner_id_;
|
||||||
}
|
}
|
||||||
const std::vector<Type>& items() const {
|
|
||||||
|
iterator begin() override {
|
||||||
resolve();
|
resolve();
|
||||||
return items_;
|
return items_.begin();
|
||||||
}
|
}
|
||||||
std::vector<Type>& items() {
|
iterator end() override {
|
||||||
resolve();
|
resolve();
|
||||||
return items_;
|
return items_.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void push_back(const value_type& value) override {
|
||||||
|
resolve();
|
||||||
|
items_.push_back(value);
|
||||||
|
}
|
||||||
|
[[nodiscard]] size_t size() override {
|
||||||
|
resolve();
|
||||||
|
return items_.size();
|
||||||
|
}
|
||||||
|
[[nodiscard]] bool empty() override {
|
||||||
|
resolve();
|
||||||
|
return items_.empty();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resolve() {
|
void resolve() {
|
||||||
if (loaded_) {
|
if (loaded_) {
|
||||||
@@ -70,5 +171,18 @@ private:
|
|||||||
std::weak_ptr<collection_resolver<Type>> resolver_{};
|
std::weak_ptr<collection_resolver<Type>> resolver_{};
|
||||||
mutable std::mutex mutex_{};
|
mutable std::mutex mutex_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename Type, class OwnerType>
|
||||||
|
class collection_many_to_many_proxy {
|
||||||
|
public:
|
||||||
|
using value_type = Type;
|
||||||
|
using owner_type = OwnerType;
|
||||||
|
using relation_type = many_to_many_relation<value_type, owner_type>;
|
||||||
|
using iterator = typename std::vector<relation_type>::iterator;
|
||||||
|
using const_iterator = typename std::vector<relation_type>::const_iterator;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<relation_type> relations_;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_COLLECTION_PROXY_HPP
|
#endif //MATADOR_COLLECTION_PROXY_HPP
|
||||||
@@ -8,6 +8,48 @@
|
|||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
|
template < class LocalType, class ForeignType >
|
||||||
|
class relation {
|
||||||
|
public:
|
||||||
|
relation() = default;
|
||||||
|
relation(std::string local_name, std::string remote_name)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, remote_name_(std::move(remote_name)) {}
|
||||||
|
relation(std::string local_name, std::string remote_name, const object_ptr<LocalType>& local, const ForeignType& remote)
|
||||||
|
: local_name_(std::move(local_name))
|
||||||
|
, remote_name_(std::move(remote_name))
|
||||||
|
, local_(local)
|
||||||
|
, remote_(remote)
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<class Operator>
|
||||||
|
void process(Operator &op) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::belongs_to(op, local_name_.c_str(), local_, utils::CascadeNoneFetchLazy);
|
||||||
|
foreign_field(op);
|
||||||
|
}
|
||||||
|
|
||||||
|
object_ptr<LocalType> local() const { return local_; }
|
||||||
|
const ForeignType& remote() const { return remote_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<typename Operator, class RemoteType = ForeignType>
|
||||||
|
void foreign_field(Operator &op, std::enable_if_t<!is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::attribute(op, remote_name_.c_str(), remote_);
|
||||||
|
}
|
||||||
|
template<typename Operator, class RemoteType = ForeignType>
|
||||||
|
void foreign_field(Operator &op, std::enable_if_t<is_object_ptr<RemoteType>::value>* /*unused*/) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::belongs_to(op, remote_name_.c_str(), remote_, utils::CascadeNoneFetchLazy);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
std::string local_name_;
|
||||||
|
std::string remote_name_;
|
||||||
|
object_ptr<LocalType> local_;
|
||||||
|
ForeignType remote_;
|
||||||
|
};
|
||||||
|
|
||||||
template < class LocalType, class ForeignType >
|
template < class LocalType, class ForeignType >
|
||||||
class many_to_many_relation {
|
class many_to_many_relation {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -17,17 +17,21 @@ public:
|
|||||||
|
|
||||||
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
|
static const attribute& create_attribute(std::string name, const std::shared_ptr<object>& obj);
|
||||||
|
|
||||||
[[nodiscard]] attribute* primary_key_attribute() const;
|
[[nodiscard]] const attribute* primary_key_attribute() const;
|
||||||
[[nodiscard]] const utils::identifier& primary_key() const;
|
[[nodiscard]] const utils::identifier& primary_key() const;
|
||||||
[[nodiscard]] bool has_primary_key() const;
|
[[nodiscard]] bool has_primary_key() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_relation_object() const;
|
||||||
|
[[nodiscard]] const attribute* join_attribute() const;
|
||||||
|
[[nodiscard]] const attribute* inverse_join_attribute() const;
|
||||||
|
|
||||||
[[nodiscard]] const std::string& name() const;
|
[[nodiscard]] const std::string& name() const;
|
||||||
|
|
||||||
void update_name(const std::string& name);
|
void update_name(const std::string& name);
|
||||||
|
|
||||||
[[nodiscard]] bool has_attributes() const;
|
[[nodiscard]] bool has_attributes() const;
|
||||||
[[nodiscard]] size_t attribute_count() const;
|
[[nodiscard]] size_t attribute_count() const;
|
||||||
[[nodiscard]] const std::list<attribute>& attributes() const;
|
[[nodiscard]] const std::vector<attribute>& attributes() const;
|
||||||
|
|
||||||
[[nodiscard]] bool has_constraints() const;
|
[[nodiscard]] bool has_constraints() const;
|
||||||
[[nodiscard]] size_t constraint_count() const;
|
[[nodiscard]] size_t constraint_count() const;
|
||||||
@@ -40,9 +44,12 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
|
|
||||||
std::string name_;
|
std::string name_;
|
||||||
attribute* pk_attribute_{nullptr};
|
int pk_column_index_{-1};
|
||||||
|
int join_column_index_{-1};
|
||||||
|
int inverse_join_column_index_{-1};
|
||||||
|
|
||||||
utils::identifier pk_identifier_;
|
utils::identifier pk_identifier_;
|
||||||
std::list<attribute> attributes_;
|
std::vector<attribute> attributes_;
|
||||||
std::list<restriction> constraints_;
|
std::list<restriction> constraints_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "matador/object/object_resolver.hpp"
|
#include "matador/object/object_resolver.hpp"
|
||||||
|
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/message_bus.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -35,6 +36,23 @@ struct cache_entry : cache_entry_base {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct object_cache_event {
|
||||||
|
std::type_index type{typeid(void)};
|
||||||
|
utils::identifier id{};
|
||||||
|
std::chrono::steady_clock::time_point timestamp{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct object_cache_accessed_event : object_cache_event {};
|
||||||
|
struct object_cache_proxy_added_event : object_cache_event {};
|
||||||
|
struct object_cache_entity_attached_event : object_cache_event {};
|
||||||
|
struct object_cache_imported_event : object_cache_event {};
|
||||||
|
struct object_cache_erased_event : object_cache_event {};
|
||||||
|
|
||||||
|
struct object_cache_sweep_event {
|
||||||
|
std::size_t removed{};
|
||||||
|
std::chrono::steady_clock::time_point timestamp{};
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
|
* @brief Thread-sicherer Cache für Objekt-Proxies und (optional) geladene Entities.
|
||||||
*
|
*
|
||||||
@@ -54,7 +72,8 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Erzeugt einen leeren Cache.
|
* @brief Erzeugt einen leeren Cache.
|
||||||
*/
|
*/
|
||||||
object_cache() = default;
|
explicit object_cache(utils::message_bus &bus)
|
||||||
|
: bus_(bus) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
|
* @brief Liefert einen Proxy für (T, id) und erstellt ihn bei Bedarf.
|
||||||
@@ -83,6 +102,7 @@ public:
|
|||||||
// found entry, return std::shared_ptr of proxy
|
// found entry, return std::shared_ptr of proxy
|
||||||
auto *entry = entry_cast_<Type>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
if (auto proxy_ptr = entry->proxy.lock()) {
|
if (auto proxy_ptr = entry->proxy.lock()) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,6 +116,8 @@ public:
|
|||||||
|
|
||||||
entry->proxy = proxy_ptr;
|
entry->proxy = proxy_ptr;
|
||||||
|
|
||||||
|
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
// return the shared_ptr of the proxy
|
// return the shared_ptr of the proxy
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
@@ -117,10 +139,65 @@ public:
|
|||||||
entry->proxy = proxy_ptr;
|
entry->proxy = proxy_ptr;
|
||||||
map_.emplace(k, std::move(entry_ptr));
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
|
||||||
|
bus_.publish<object_cache_proxy_added_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
// return the shared_ptr of the proxy
|
// return the shared_ptr of the proxy
|
||||||
return proxy_ptr;
|
return proxy_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type, typename ResolverPointerType>
|
||||||
|
bool import(const utils::identifier &id, const std::shared_ptr<object_proxy<Type>> &proxy, ResolverPointerType &&resolver_ptr) {
|
||||||
|
if (!proxy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto obj = proxy->object();
|
||||||
|
auto weak_resolver = to_weak<Type>(std::forward<ResolverPointerType>(resolver_ptr));
|
||||||
|
const auto k = make_key<Type>(id);
|
||||||
|
|
||||||
|
std::unique_lock lock(mutex_);
|
||||||
|
|
||||||
|
auto it = map_.find(k);
|
||||||
|
if (it != map_.end()) {
|
||||||
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
|
|
||||||
|
if (auto existing_proxy = entry->proxy.lock()) {
|
||||||
|
if (existing_proxy != proxy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
proxy->primary_key(id);
|
||||||
|
proxy->resolver(std::move(weak_resolver));
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
entry->entity = obj;
|
||||||
|
proxy->attach(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->proxy = proxy;
|
||||||
|
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto entry_ptr = std::make_unique<cache_entry<Type>>();
|
||||||
|
auto *entry = entry_ptr.get();
|
||||||
|
|
||||||
|
proxy->primary_key(id);
|
||||||
|
proxy->resolver(std::move(weak_resolver));
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
entry->entity = obj;
|
||||||
|
proxy->attach(std::move(obj));
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->proxy = proxy;
|
||||||
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
|
||||||
|
bus_.publish<object_cache_imported_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
|
* @brief Verknüpft eine geladene Entity mit einem Cache-Eintrag (Type, id).
|
||||||
*
|
*
|
||||||
@@ -145,6 +222,7 @@ public:
|
|||||||
auto *entry = entry_ptr.get();
|
auto *entry = entry_ptr.get();
|
||||||
entry->entity = obj;
|
entry->entity = obj;
|
||||||
map_.emplace(k, std::move(entry_ptr));
|
map_.emplace(k, std::move(entry_ptr));
|
||||||
|
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +233,8 @@ public:
|
|||||||
proxy->attach(std::move(obj));
|
proxy->attach(std::move(obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bus_.publish<object_cache_entity_attached_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,6 +259,9 @@ public:
|
|||||||
|
|
||||||
auto *entry = entry_cast_<Type>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
auto entity_ptr = entry->entity.lock();
|
auto entity_ptr = entry->entity.lock();
|
||||||
|
if (entity_ptr) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
return entity_ptr;
|
return entity_ptr;
|
||||||
}
|
}
|
||||||
@@ -204,6 +287,9 @@ public:
|
|||||||
|
|
||||||
auto *entry = entry_cast_<Type>(it->second.get());
|
auto *entry = entry_cast_<Type>(it->second.get());
|
||||||
const bool loaded = !entry->entity.expired();
|
const bool loaded = !entry->entity.expired();
|
||||||
|
if (loaded) {
|
||||||
|
bus_.publish<object_cache_accessed_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
prune_if_dead(it);
|
prune_if_dead(it);
|
||||||
return loaded;
|
return loaded;
|
||||||
}
|
}
|
||||||
@@ -224,6 +310,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
map_.erase(it);
|
map_.erase(it);
|
||||||
|
bus_.publish<object_cache_erased_event>({k.type, id, std::chrono::steady_clock::now()});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,6 +333,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (removed > 0) {
|
||||||
|
bus_.publish<object_cache_sweep_event>({removed, std::chrono::steady_clock::now()});
|
||||||
|
}
|
||||||
|
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,6 +416,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::shared_mutex mutex_{};
|
mutable std::shared_mutex mutex_{};
|
||||||
|
utils::message_bus &bus_;
|
||||||
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
|
std::unordered_map<key, std::unique_ptr<cache_entry_base>, key_hash> map_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,11 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template < class Type >
|
template < class Type >
|
||||||
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t, basic_repository &repo, const std::string &name) {
|
static std::shared_ptr<object> generate(std::unique_ptr<Type>&& t,
|
||||||
|
basic_repository &repo,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &join_column = "",
|
||||||
|
const std::string &inverse_join_column = "") {
|
||||||
const std::type_index ti(typeid(Type));
|
const std::type_index ti(typeid(Type));
|
||||||
if (repo.has_object_for_type(ti)) {
|
if (repo.has_object_for_type(ti)) {
|
||||||
auto obj = repo.object_for_type(ti);
|
auto obj = repo.object_for_type(ti);
|
||||||
@@ -71,6 +75,9 @@ public:
|
|||||||
std::ignore = repo.provide_object_in_advance(ti, obj);
|
std::ignore = repo.provide_object_in_advance(ti, obj);
|
||||||
object_generator gen(repo, obj);
|
object_generator gen(repo, obj);
|
||||||
access::process(gen, *t);
|
access::process(gen, *t);
|
||||||
|
if (!join_column.empty() && !inverse_join_column.empty()) {
|
||||||
|
gen.prepare_relation_table(join_column, inverse_join_column);
|
||||||
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +105,7 @@ public:
|
|||||||
void on_foreign_key(const char *id, Pointer &/*x*/) {
|
void on_foreign_key(const char *id, Pointer &/*x*/) {
|
||||||
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
|
const auto type = pk_type_determinator::determine<typename Pointer::value_type>();
|
||||||
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
|
auto &ref = object_->attributes_.emplace_back(id, type, utils::constraints::ForeignKey, null_option_type::NotNull);
|
||||||
|
ref.index_ = object_->attributes_.size() - 1;
|
||||||
ref.owner_ = object_;
|
ref.owner_ = object_;
|
||||||
}
|
}
|
||||||
template<class ContainerType>
|
template<class ContainerType>
|
||||||
@@ -111,6 +119,7 @@ private:
|
|||||||
template<typename ValueType>
|
template<typename ValueType>
|
||||||
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
|
attribute &emplace_attribute(const char *id, const utils::field_attributes& attr, null_option_type null_option) {
|
||||||
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
|
auto &ref = object_->attributes_.emplace_back(id, utils::data_type_traits<ValueType>::type(attr.size()), attr, null_option);
|
||||||
|
ref.index_ = object_->attributes_.size() - 1;
|
||||||
ref.owner_ = object_;
|
ref.owner_ = object_;
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
@@ -120,9 +129,10 @@ private:
|
|||||||
void create_fk_constraint(const std::string& name) const;
|
void create_fk_constraint(const std::string& name) const;
|
||||||
void create_unique_constraint(const std::string& name) const;
|
void create_unique_constraint(const std::string& name) const;
|
||||||
|
|
||||||
[[nodiscard]] std::list<attribute>::iterator find_attribute_by_name(const std::string &name) const;
|
[[nodiscard]] std::vector<attribute>::iterator find_attribute_by_name(const std::string &name) const;
|
||||||
|
|
||||||
void prepare_primary_key(attribute &ref, utils::identifier &&pk) const;
|
void prepare_primary_key(const attribute &ref, utils::identifier &&pk) const;
|
||||||
|
void prepare_relation_table(const std::string &join_column, const std::string &inverse_join_column) const;
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
[[nodiscard]] std::shared_ptr<object> fk_object() const;
|
[[nodiscard]] std::shared_ptr<object> fk_object() const;
|
||||||
@@ -135,7 +145,7 @@ 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) {
|
||||||
utils::constraints cs = utils::constraints::PrimaryKey;
|
auto cs = utils::constraints::PrimaryKey;
|
||||||
if (attr.generator() == utils::generator_type::Identity) {
|
if (attr.generator() == utils::generator_type::Identity) {
|
||||||
cs |= utils::constraints::Identity;
|
cs |= utils::constraints::Identity;
|
||||||
}
|
}
|
||||||
@@ -160,7 +170,7 @@ void object_generator::create_fk_constraint(const std::string& name) const {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto obj = fk_object<Type>();
|
const auto obj = fk_object<Type>();
|
||||||
restriction pk_constraint(*pk_attr);
|
restriction pk_constraint(pk_attr->index());
|
||||||
pk_constraint.options_ |= utils::constraints::ForeignKey;
|
pk_constraint.options_ |= utils::constraints::ForeignKey;
|
||||||
pk_constraint.owner_ = object_;
|
pk_constraint.owner_ = object_;
|
||||||
pk_constraint.reference_ = obj;
|
pk_constraint.reference_ = obj;
|
||||||
|
|||||||
@@ -26,87 +26,164 @@ public:
|
|||||||
|
|
||||||
// Lazy
|
// Lazy
|
||||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
||||||
: resolver_(resolver)
|
: resolver_(std::move(resolver))
|
||||||
, pk_(std::move(id)) {
|
, pk_(std::move(id))
|
||||||
|
, state_(object_state::Persistent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Eager
|
// Eager
|
||||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
||||||
: obj_(obj)
|
: obj_(std::move(obj))
|
||||||
, resolver_(resolver)
|
, resolver_(std::move(resolver))
|
||||||
, pk_(primary_key_resolver::resolve_object(*obj).pk)
|
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{})
|
||||||
, state_(object_state::Persistent){
|
, state_(obj_ ? object_state::Persistent : object_state::Detached) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transient
|
// Transient
|
||||||
explicit object_proxy(std::shared_ptr<Type> obj)
|
explicit object_proxy(std::shared_ptr<Type> obj)
|
||||||
: obj_(obj)
|
: obj_(std::move(obj))
|
||||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {
|
, pk_(obj_ ? primary_key_resolver::resolve_object(*obj_).pk : utils::identifier{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void attach(std::shared_ptr<Type> obj) {
|
void attach(std::shared_ptr<Type> obj) {
|
||||||
std::lock_guard lock(mutex_);
|
std::lock_guard lock(mutex_);
|
||||||
|
|
||||||
obj_ = std::move(obj);
|
obj_ = std::move(obj);
|
||||||
if (obj_) {
|
if (!obj_) {
|
||||||
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
|
pk_.clear();
|
||||||
state_.store(object_state::Persistent, std::memory_order_release);
|
state_ = object_state::Detached;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pk_ = primary_key_resolver::resolve_object(*obj_).pk;
|
||||||
|
state_ = object_state::Persistent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resolver(std::weak_ptr<object_resolver<Type>> resolver) {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
resolver_ = std::move(resolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||||
|
return resolve_object();
|
||||||
}
|
}
|
||||||
|
|
||||||
void invalidate() {
|
void invalidate() {
|
||||||
std::lock_guard lock(mutex_);
|
std::lock_guard lock(mutex_);
|
||||||
obj_.reset();
|
obj_.reset();
|
||||||
resolver_.reset();
|
resolver_.reset();
|
||||||
state_.store(object_state::Detached, std::memory_order_release);
|
state_ = object_state::Detached;
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
||||||
|
|
||||||
Type *operator->() { return pointer(); }
|
Type *operator->() {
|
||||||
Type &operator*() { return *pointer(); }
|
auto *ptr = pointer();
|
||||||
const Type &operator*() const { return *pointer(); }
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
Type *pointer() const { return resolve(); }
|
const Type *operator->() const {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type &operator*() {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type &operator*() const {
|
||||||
|
auto *ptr = pointer();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object proxy");
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type *pointer() const {
|
||||||
|
return resolve_object().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return !obj_ && resolver_.expired();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const { return !obj_ && resolver_.expired(); }
|
|
||||||
[[nodiscard]] bool valid() const { return !empty(); }
|
[[nodiscard]] bool valid() const { return !empty(); }
|
||||||
[[nodiscard]] bool has_primary_key() const { return !pk_.is_null(); }
|
|
||||||
[[nodiscard]] const utils::identifier &primary_key() const { return pk_; }
|
[[nodiscard]] bool has_primary_key() const {
|
||||||
void primary_key(const utils::identifier &pk) { pk_ = pk; }
|
std::lock_guard lock(mutex_);
|
||||||
|
return !pk_.is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] utils::identifier primary_key() const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return pk_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void primary_key(const utils::identifier &pk) {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
pk_ = pk;
|
||||||
|
}
|
||||||
|
|
||||||
bool is_persistent() const { return is_state(object_state::Persistent); }
|
bool is_persistent() const { return is_state(object_state::Persistent); }
|
||||||
bool is_transient() const { return is_state(object_state::Transient); }
|
bool is_transient() const { return is_state(object_state::Transient); }
|
||||||
bool is_detached() const { return is_state(object_state::Detached); }
|
bool is_detached() const { return is_state(object_state::Detached); }
|
||||||
bool is_removed() const { return is_state(object_state::Removed); }
|
bool is_removed() const { return is_state(object_state::Removed); }
|
||||||
bool is_state(const object_state state) const { return state_ == state; }
|
|
||||||
|
bool is_state(const object_state state) const {
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
return state_ == state;
|
||||||
|
}
|
||||||
|
|
||||||
void change_state(const object_state state) {
|
void change_state(const object_state state) {
|
||||||
state_.store(state, std::memory_order_release);
|
std::lock_guard lock(mutex_);
|
||||||
|
state_ = state;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Type* resolve() const {
|
std::shared_ptr<Type> resolve_object() const {
|
||||||
|
std::shared_ptr<Type> current;
|
||||||
|
std::shared_ptr<object_resolver<Type>> resolver;
|
||||||
|
utils::identifier pk;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
if (obj_) {
|
if (obj_) {
|
||||||
return obj_.get();
|
return obj_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::lock_guard lock(mutex_);
|
resolver = resolver_.lock();
|
||||||
auto resolver = resolver_.lock();
|
|
||||||
if (!resolver) {
|
if (!resolver) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
// Todo: Add states (Detached, Attached, Transient) - if attached an no resolver is available throw runtime exception
|
|
||||||
// throw std::runtime_error("Detached proxy (session expired)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const_cast<std::shared_ptr<Type>&>(obj_) = resolver->resolve(pk_);
|
pk = pk_;
|
||||||
|
|
||||||
return obj_.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current = resolver->resolve(pk);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex_);
|
||||||
|
if (!obj_) {
|
||||||
|
obj_ = std::move(current);
|
||||||
|
}
|
||||||
|
return obj_;
|
||||||
|
}
|
||||||
|
}
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Type> obj_{};
|
mutable std::shared_ptr<Type> obj_{};
|
||||||
std::weak_ptr<object_resolver<Type>> resolver_{};
|
mutable std::weak_ptr<object_resolver<Type>> resolver_{};
|
||||||
utils::identifier pk_{};
|
utils::identifier pk_{};
|
||||||
std::atomic<object_state> state_{object_state::Transient};
|
object_state state_{object_state::Transient};
|
||||||
mutable std::mutex mutex_{};
|
mutable std::mutex mutex_{};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,62 +19,148 @@ class object_ptr {
|
|||||||
public:
|
public:
|
||||||
object_ptr()
|
object_ptr()
|
||||||
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
||||||
|
|
||||||
object_ptr(null_object_ptr_t) {}
|
object_ptr(null_object_ptr_t) {}
|
||||||
|
|
||||||
explicit object_ptr(std::shared_ptr<Type> obj)
|
explicit object_ptr(std::shared_ptr<Type> obj)
|
||||||
: proxy_(std::make_shared<object_proxy<Type>>(obj)) {}
|
: proxy_(std::make_shared<object_proxy<Type>>(std::move(obj))) {}
|
||||||
|
|
||||||
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
||||||
: proxy_(std::move(obj)) {}
|
: proxy_(std::move(obj)) {}
|
||||||
|
|
||||||
object_ptr(const object_ptr &other) = default;
|
object_ptr(const object_ptr &other) = default;
|
||||||
object_ptr(object_ptr &&other) noexcept = default;
|
object_ptr(object_ptr &&other) noexcept = default;
|
||||||
object_ptr& operator=(const object_ptr &other) = default;
|
object_ptr& operator=(const object_ptr &other) = default;
|
||||||
object_ptr& operator=(object_ptr &&other) = default;
|
object_ptr& operator=(object_ptr &&other) noexcept = default;
|
||||||
|
|
||||||
object_ptr& operator=(null_object_ptr_t) {
|
object_ptr& operator=(null_object_ptr_t) {
|
||||||
proxy_.reset();
|
proxy_.reset();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const object_ptr &other) const {
|
bool operator==(const object_ptr &other) const {
|
||||||
return get() == other.get();
|
if (proxy_ == other.proxy_) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!proxy_ || !other.proxy_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_primary_key() && other.has_primary_key()) {
|
||||||
|
return primary_key() == other.primary_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool operator==(null_object_ptr_t) const {
|
bool operator==(null_object_ptr_t) const {
|
||||||
return empty();
|
return empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
||||||
bool operator!=(null_object_ptr_t) const { return !empty(); }
|
bool operator!=(null_object_ptr_t) const { return !empty(); }
|
||||||
|
|
||||||
using value_type = Type;
|
using value_type = Type;
|
||||||
|
|
||||||
Type *operator->() const { return get(); }
|
Type *operator->() const {
|
||||||
Type &operator*() { return *get(); }
|
return checked_get();
|
||||||
const Type &operator*() const { return *get(); }
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool empty() const { return get() == nullptr; }
|
Type &operator*() {
|
||||||
|
return *checked_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Type &operator*() const {
|
||||||
|
return *checked_get();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool empty() const {
|
||||||
|
return proxy_ == nullptr || proxy_->empty();
|
||||||
|
}
|
||||||
|
|
||||||
Type *get() const {
|
Type *get() const {
|
||||||
return proxy_ ? proxy_->pointer() : nullptr;
|
return proxy_ ? proxy_->pointer() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset() { proxy_.reset(); }
|
[[nodiscard]] std::shared_ptr<Type> object() const {
|
||||||
void reset(const std::shared_ptr<object_proxy<Type>>& proxy) { proxy_ = proxy; }
|
return proxy_ ? proxy_->object() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
operator bool() const { return valid(); }
|
void reset() {
|
||||||
[[nodiscard]] bool valid() const { return proxy_ != nullptr && !proxy_->empty(); }
|
proxy_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); }
|
void reset(std::shared_ptr<object_proxy<Type>> proxy) {
|
||||||
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); }
|
proxy_ = std::move(proxy);
|
||||||
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); }
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool is_persistent() const { return proxy_->is_persistent(); }
|
[[nodiscard]] std::shared_ptr<object_proxy<Type>> proxy() const {
|
||||||
[[nodiscard]] bool is_transient() const { return proxy_->is_transient(); }
|
return proxy_;
|
||||||
[[nodiscard]] bool is_detached() const { return proxy_->is_detached(); }
|
}
|
||||||
[[nodiscard]] bool is_removed() const { return proxy_->is_removed(); }
|
|
||||||
[[nodiscard]] bool is_state(const object_state state) const { return proxy_->is_state(state); }
|
explicit operator bool() const {
|
||||||
|
return valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool valid() const {
|
||||||
|
return proxy_ != nullptr && !proxy_->empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_primary_key() const {
|
||||||
|
return proxy_ != nullptr && proxy_->has_primary_key();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] utils::identifier primary_key() const {
|
||||||
|
return proxy_ ? proxy_->primary_key() : utils::identifier{};
|
||||||
|
}
|
||||||
|
|
||||||
|
void primary_key(const utils::identifier &pk) {
|
||||||
|
ensure_proxy();
|
||||||
|
proxy_->primary_key(pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_persistent() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_persistent();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_transient() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_transient();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_detached() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_detached();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_removed() const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_removed();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool is_state(const object_state state) const {
|
||||||
|
return proxy_ != nullptr && proxy_->is_state(state);
|
||||||
|
}
|
||||||
|
|
||||||
void change_state(object_state s) const {
|
void change_state(object_state s) const {
|
||||||
if (proxy_) {
|
if (proxy_) {
|
||||||
proxy_->change_state(s);
|
proxy_->change_state(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type *checked_get() const {
|
||||||
|
auto *ptr = get();
|
||||||
|
if (!ptr) {
|
||||||
|
throw std::runtime_error("Cannot dereference empty object_ptr");
|
||||||
|
}
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ensure_proxy() {
|
||||||
|
if (!proxy_) {
|
||||||
|
proxy_ = std::make_shared<object_proxy<Type>>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<object_proxy<Type>> proxy_{};
|
std::shared_ptr<object_proxy<Type>> proxy_{};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ class null_observer : public observer<Type> {
|
|||||||
public:
|
public:
|
||||||
template < class OtherType >
|
template < class OtherType >
|
||||||
explicit null_observer(const null_observer<OtherType> *) {}
|
explicit null_observer(const null_observer<OtherType> *) {}
|
||||||
void on_attach(repository_node &, Type &) override {}
|
void on_attach(const repository_node &, const Type &) const override {}
|
||||||
void on_detach(repository_node &, Type &) override {}
|
void on_detach(const repository_node &, const Type &) const override {}
|
||||||
void on_insert(Type &) override {}
|
void on_insert(Type &) override {}
|
||||||
void on_update(Type &) override {}
|
void on_update(Type &) override {}
|
||||||
void on_delete(Type &) override {}
|
void on_delete(Type &) override {}
|
||||||
|
|||||||
@@ -88,9 +88,11 @@ struct pk_field_locator {
|
|||||||
desc.kind = pk_kind::uuid;
|
desc.kind = pk_kind::uuid;
|
||||||
|
|
||||||
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
desc.is_known_at = [](void *obj, const std::size_t off) -> bool {
|
||||||
auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
const auto *p = reinterpret_cast<uuid16 *>(static_cast<std::uint8_t *>(obj) + off);
|
||||||
// “unknown” = all zeros
|
// “unknown” = all zeros
|
||||||
for (auto b: *p) { if (b != 0) return true; }
|
for (const auto b: *p) {
|
||||||
|
if (b != 0) return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ void relation_completer<Type, Observers...>::on_has_many(const char *id, Collect
|
|||||||
using relation_value_type = many_to_many_relation<Type, value_type>;
|
using relation_value_type = many_to_many_relation<Type, value_type>;
|
||||||
|
|
||||||
// Check if the object_ptr type is already inserted in the schema (by id)
|
// Check if the object_ptr type is already inserted in the schema (by id)
|
||||||
auto foreign_node = find_node(typeid(value_type));
|
const auto foreign_node = find_node(typeid(value_type));
|
||||||
if (!foreign_node) {
|
if (!foreign_node) {
|
||||||
// Todo: throw internal error or attach node
|
// Todo: throw internal error or attach node
|
||||||
return;
|
return;
|
||||||
@@ -191,7 +191,7 @@ void relation_completer<Type, Observers...>::on_has_many(const char *id, Collect
|
|||||||
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
|
const auto local_endpoint = std::make_shared<relation_endpoint>(id, relation_type::HasMany, *foreign_node);
|
||||||
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
|
nodes_.top()->info_->register_relation_endpoint(typeid(value_type), local_endpoint);
|
||||||
} else {
|
} else {
|
||||||
// A relation table is necessary
|
// A relation table is necessary.
|
||||||
// Endpoint was not found.
|
// Endpoint was not found.
|
||||||
// Always attach a many-to-many relation type. If later a
|
// Always attach a many-to-many relation type. If later a
|
||||||
// belongs-to relation handles this relation, the many-to-many
|
// belongs-to relation handles this relation, the many-to-many
|
||||||
@@ -222,8 +222,7 @@ template<typename Type, template<typename> typename... Observers>
|
|||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &, const char *join_column,
|
void relation_completer<Type, Observers...>::on_has_many(const char *id, CollectionType &, const char *join_column,
|
||||||
const utils::foreign_attributes &,
|
const utils::foreign_attributes &,
|
||||||
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>
|
std::enable_if_t<!is_object_ptr<typename CollectionType::value_type>::value>* /*unused*/) {
|
||||||
* /*unused*/) {
|
|
||||||
using value_type = typename CollectionType::value_type;
|
using value_type = typename CollectionType::value_type;
|
||||||
using relation_value_type = many_to_relation<Type, value_type>;
|
using relation_value_type = many_to_relation<Type, value_type>;
|
||||||
|
|
||||||
@@ -367,7 +366,12 @@ void relation_completer<Type, Observers...>::attach_relation_node(const std::str
|
|||||||
|
|
||||||
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
|
auto observers = internal::observer_list_copy_creator<Type, relation_value_type, Observers...>::copy_create(observers_);
|
||||||
|
|
||||||
auto node = repository_node::make_node<relation_value_type>(repo_, name, std::move(creator), std::move(observers));
|
auto node = repository_node::make_relation_node<relation_value_type>(repo_,
|
||||||
|
name,
|
||||||
|
join_column,
|
||||||
|
inverse_join_column,
|
||||||
|
std::move(creator),
|
||||||
|
std::move(observers));
|
||||||
auto result = repo_.attach_node(node.release(), "");
|
auto result = repo_.attach_node(node.release(), "");
|
||||||
if (!result) {
|
if (!result) {
|
||||||
// Todo: throw internal error
|
// Todo: throw internal error
|
||||||
|
|||||||
@@ -23,6 +23,13 @@ public:
|
|||||||
const std::string& name,
|
const std::string& name,
|
||||||
creator_func<Type> creator,
|
creator_func<Type> creator,
|
||||||
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
||||||
|
template < typename Type, template<typename> typename... Observers >
|
||||||
|
static std::unique_ptr<repository_node> make_relation_node(basic_repository& repo,
|
||||||
|
const std::string& name,
|
||||||
|
const std::string& join_column,
|
||||||
|
const std::string& inverse_join_column,
|
||||||
|
creator_func<Type> creator,
|
||||||
|
std::vector<std::unique_ptr<observer<Type>>>&& observers);
|
||||||
|
|
||||||
explicit repository_node(basic_repository& repo);
|
explicit repository_node(basic_repository& repo);
|
||||||
repository_node(const repository_node& other) = delete;
|
repository_node(const repository_node& other) = delete;
|
||||||
@@ -101,5 +108,28 @@ std::unique_ptr<repository_node> repository_node::make_node(basic_repository &re
|
|||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Type, template <typename> class ... Observers>
|
||||||
|
std::unique_ptr<repository_node> repository_node::make_relation_node(basic_repository &repo,
|
||||||
|
const std::string &name,
|
||||||
|
const std::string &join_column,
|
||||||
|
const std::string &inverse_join_column,
|
||||||
|
creator_func<Type> creator,
|
||||||
|
std::vector<std::unique_ptr<observer<Type>>> &&observers) {
|
||||||
|
const std::type_index ti(typeid(Type));
|
||||||
|
auto node = std::unique_ptr<repository_node>(new repository_node(repo, name, ti));
|
||||||
|
|
||||||
|
internal::observer_list_creator<Type, Observers...>::create_missing(observers);
|
||||||
|
|
||||||
|
auto obj = object_generator::generate<Type>(creator(), repo, name, join_column, inverse_join_column);
|
||||||
|
node->info_.reset(std::make_unique<object_info<Type>>(
|
||||||
|
*node,
|
||||||
|
obj,
|
||||||
|
std::move(observers),
|
||||||
|
std::forward<creator_func<Type>>(creator)
|
||||||
|
).release());
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif //REPOSITORY_NODE_HPP
|
#endif //REPOSITORY_NODE_HPP
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ class object;
|
|||||||
|
|
||||||
class restriction {
|
class restriction {
|
||||||
public:
|
public:
|
||||||
explicit restriction(const class attribute& attr);
|
explicit restriction(size_t attr_index);
|
||||||
|
|
||||||
[[nodiscard]] const class attribute& attribute() const;
|
[[nodiscard]] size_t attribute_index() const;
|
||||||
[[nodiscard]] std::string column_name() const;
|
[[nodiscard]] std::string column_name() const;
|
||||||
|
[[nodiscard]] utils::constraints options() const;
|
||||||
[[nodiscard]] std::shared_ptr<object> owner() const;
|
[[nodiscard]] std::shared_ptr<object> owner() const;
|
||||||
[[nodiscard]] bool is_primary_key_constraint() const;
|
[[nodiscard]] bool is_primary_key_constraint() const;
|
||||||
[[nodiscard]] bool is_foreign_key_constraint() const;
|
[[nodiscard]] bool is_foreign_key_constraint() const;
|
||||||
[[nodiscard]] bool is_unique_constraint() const;
|
[[nodiscard]] bool is_unique_constraint() const;
|
||||||
[[nodiscard]] std::string ref_table_name() const;
|
[[nodiscard]] std::string ref_table_name() const;
|
||||||
[[nodiscard]] std::string ref_column_name() const;
|
[[nodiscard]] std::string ref_column_name() const;
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream& os, const restriction& c);
|
friend std::ostream& operator<<(std::ostream& os, const restriction& c);
|
||||||
|
|
||||||
[[nodiscard]] std::string type_string() const;
|
[[nodiscard]] std::string type_string() const;
|
||||||
@@ -37,7 +37,7 @@ private:
|
|||||||
friend class object_generator;
|
friend class object_generator;
|
||||||
friend class object;
|
friend class object;
|
||||||
|
|
||||||
const class attribute& attr_;
|
const size_t index_;
|
||||||
std::weak_ptr<object> owner_;
|
std::weak_ptr<object> owner_;
|
||||||
std::weak_ptr<object> reference_;
|
std::weak_ptr<object> reference_;
|
||||||
utils::constraints options_{utils::constraints::None};
|
utils::constraints options_{utils::constraints::None};
|
||||||
|
|||||||
@@ -3,17 +3,13 @@
|
|||||||
|
|
||||||
#include "matador/utils/attribute_writer.hpp"
|
#include "matador/utils/attribute_writer.hpp"
|
||||||
|
|
||||||
#include <optional>
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
namespace matador::sql {
|
#include <optional>
|
||||||
class dialect;
|
|
||||||
class connection_impl;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|
||||||
class attribute_string_writer final : public utils::attribute_writer
|
class attribute_string_writer final : public utils::attribute_writer {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
attribute_string_writer(const sql::dialect &d, std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
||||||
|
|
||||||
|
|||||||
@@ -8,15 +8,16 @@
|
|||||||
#include "matador/utils/basic_types.hpp"
|
#include "matador/utils/basic_types.hpp"
|
||||||
#include "matador/utils/constraints.hpp"
|
#include "matador/utils/constraints.hpp"
|
||||||
#include "matador/utils/data_type_traits.hpp"
|
#include "matador/utils/data_type_traits.hpp"
|
||||||
|
#include "matador/utils/types.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
|
|
||||||
class data_type {
|
class data_type {
|
||||||
public:
|
public:
|
||||||
explicit data_type(const utils::basic_type type, const size_t size = 0)
|
explicit data_type(const utils::basic_type type, const size_t size = 0)
|
||||||
: type_(type), size_(size) {}
|
: type_(type), size_(size) {
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] const utils::basic_type &type() const { return type_; }
|
[[nodiscard]] const utils::basic_type &type() const { return type_; }
|
||||||
[[nodiscard]] size_t size() const { return size_; }
|
[[nodiscard]] size_t size() const { return size_; }
|
||||||
@@ -30,14 +31,16 @@ template<typename Type>
|
|||||||
class typed_data_type final : public data_type {
|
class typed_data_type final : public data_type {
|
||||||
public:
|
public:
|
||||||
typed_data_type()
|
typed_data_type()
|
||||||
: data_type(utils::data_type_traits<Type>::type()) {}
|
: data_type(utils::data_type_traits<Type>::type()) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
class sized_typed_data_type final : public data_type {
|
class sized_typed_data_type final : public data_type {
|
||||||
public:
|
public:
|
||||||
explicit sized_typed_data_type(size_t size)
|
explicit sized_typed_data_type(size_t size)
|
||||||
: data_type(utils::data_type_traits<Type>::type(size), size) {}
|
: data_type(utils::data_type_traits<Type>::type(size), size) {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
using TinyInt = typed_data_type<int8_t>;
|
using TinyInt = typed_data_type<int8_t>;
|
||||||
@@ -57,9 +60,12 @@ using Boolean = typed_data_type<bool>;
|
|||||||
using Varchar = sized_typed_data_type<std::string>;
|
using Varchar = sized_typed_data_type<std::string>;
|
||||||
using Blob = sized_typed_data_type<std::vector<std::byte> >;
|
using Blob = sized_typed_data_type<std::vector<std::byte> >;
|
||||||
|
|
||||||
|
using Time = typed_data_type<utils::time_type_t>;
|
||||||
|
using Date = typed_data_type<utils::date_type_t>;
|
||||||
|
using Timestamp = typed_data_type<utils::timestamp_type_t>;
|
||||||
}
|
}
|
||||||
namespace matador::query {
|
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
class column_builder {
|
class column_builder {
|
||||||
public:
|
public:
|
||||||
explicit column_builder(std::string column_name, utils::basic_type type, size_t size = 0);
|
explicit column_builder(std::string column_name, utils::basic_type type, size_t size = 0);
|
||||||
@@ -111,6 +117,5 @@ private:
|
|||||||
constraint_builder constraint(std::string name);
|
constraint_builder constraint(std::string name);
|
||||||
// table_builder table(std::string name);
|
// table_builder table(std::string name);
|
||||||
column_builder column(std::string name, utils::basic_type type, size_t size = 0);
|
column_builder column(std::string name, utils::basic_type type, size_t size = 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif //MATADOR_BUILDER_HPP
|
#endif //MATADOR_BUILDER_HPP
|
||||||
@@ -8,12 +8,12 @@
|
|||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
enum class binary_operator {
|
enum class binary_operator {
|
||||||
EQUALS,
|
Equals,
|
||||||
NOT_EQUALS,
|
NotEquals,
|
||||||
GREATER_THAN,
|
GreaterThan,
|
||||||
GREATER_THAN_OR_EQUAL,
|
GreaterThanOrEqual,
|
||||||
LESS_THAN,
|
LessThan,
|
||||||
LESS_THAN_OR_EQUAL,
|
LessThanOrEqual,
|
||||||
};
|
};
|
||||||
|
|
||||||
class binary_criteria final : public abstract_column_criteria {
|
class binary_criteria final : public abstract_column_criteria {
|
||||||
|
|||||||
@@ -20,33 +20,33 @@ class table_column;
|
|||||||
template<class Type>
|
template<class Type>
|
||||||
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
||||||
operator==(const table_column &col, Type val) {
|
operator==(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::EQUALS, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::Equals, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
std::enable_if_t<!std::is_base_of_v<fetchable_query, std::decay_t<Type>>, criteria_ptr>
|
||||||
operator!=(const table_column &col, Type val) {
|
operator!=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::NOT_EQUALS, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::NotEquals, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator>(const table_column &col, Type val) {
|
criteria_ptr operator>(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThan, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator>=(const table_column &col, Type val) {
|
criteria_ptr operator>=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN_OR_EQUAL, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThanOrEqual, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator<(const table_column &col, Type val) {
|
criteria_ptr operator<(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThan, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
criteria_ptr operator<=(const table_column &col, Type val) {
|
criteria_ptr operator<=(const table_column &col, Type val) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN_OR_EQUAL, utils::value(val));
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThanOrEqual, utils::value(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator==(const table_column &col_left, const table_column &col_right);
|
criteria_ptr operator==(const table_column &col_left, const table_column &col_right);
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
#ifndef MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
|
#define MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/basic_schema.hpp"
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/delete_step.hpp"
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
#include "matador/query/query_builder_utils.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
#include "matador/utils/error.hpp"
|
||||||
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/primary_key_accessor.hpp"
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename ObjectType>
|
||||||
|
class delete_step_processor {
|
||||||
|
public:
|
||||||
|
explicit delete_step_processor(query_builder_context &ctx)
|
||||||
|
: ctx_(ctx) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
|
||||||
|
if (!ptr) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_ = ptr;
|
||||||
|
|
||||||
|
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
|
||||||
|
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
ctx_.visited_.insert(key);
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(typeid(ObjectType));
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto &info = it->second.node().info(); !info.has_primary_key()) {
|
||||||
|
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
access::process(*this, *ptr_);
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (as_relation_step) {
|
||||||
|
ctx_.relation_steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
|
||||||
|
} else {
|
||||||
|
ctx_.steps_.push_back(std::make_unique<delete_step_object<ObjectType>>(cit->second.delete_one, ptr_));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr_.reset();
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class PrimaryKeyType>
|
||||||
|
static void on_primary_key(const char * /*id*/, PrimaryKeyType &, const utils::primary_key_attribute & /*attr*/) {}
|
||||||
|
|
||||||
|
static void on_revision(const char * /*id*/, uint64_t & /*rev*/) {}
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
static void on_attribute(const char * /*id*/, Type &, const utils::field_attributes & /*attr*/) {}
|
||||||
|
|
||||||
|
template<class Pointer>
|
||||||
|
void on_belongs_to(const char * /*id*/, Pointer &obj, const utils::foreign_attributes &attr) {
|
||||||
|
on_foreign_object(obj, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Pointer>
|
||||||
|
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
|
on_foreign_object(obj, attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CollectionType>
|
||||||
|
void on_has_many(const char * /*id*/,
|
||||||
|
object::collection<object::object_ptr<CollectionType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_step_processor<CollectionType> processor{ctx_};
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class CollectionType>
|
||||||
|
static void on_has_many(const char * /*id*/,
|
||||||
|
object::collection<CollectionType> & /*objects*/,
|
||||||
|
const char * /*join_column*/,
|
||||||
|
const utils::foreign_attributes & /*attr*/) {
|
||||||
|
// Value-Collections bzw. Relationstabellen werden hier nicht direkt gelöscht.
|
||||||
|
// Dafür wird das Delete-Statement der jeweiligen Entity verwendet.
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const char *inverse_join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
on_many_to_many_objects<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, local_type, foreign_type};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class ForeignType>
|
||||||
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object::join_columns_collector collector;
|
||||||
|
if (auto join_columns = collector.collect<ForeignType>(); join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using relation_value_type = object::many_to_many_relation<ForeignType, ObjectType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
|
on_many_to_many_objects<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, foreign_type, local_type};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class PointerType>
|
||||||
|
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove) || !obj) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete_step_processor<PointerType> processor{ctx_};
|
||||||
|
|
||||||
|
auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class LocalType, class ForeignType, class RelationKeyFactory>
|
||||||
|
void on_many_to_many_objects(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr,
|
||||||
|
RelationKeyFactory make_relation_key) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Remove)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = make_relation_key(id);
|
||||||
|
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
|
||||||
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index()); cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
||||||
|
std::vector<std::unique_ptr<execute_step>> delete_relation_steps;
|
||||||
|
delete_step_processor<ForeignType> processor{ctx_};
|
||||||
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.is_persistent()) {
|
||||||
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
query_builder_context &ctx_;
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class ObjectType>
|
||||||
|
class delete_query_builder {
|
||||||
|
public:
|
||||||
|
explicit delete_query_builder(const basic_schema &schema,
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
||||||
|
: schema_(schema)
|
||||||
|
, contexts_by_type_(contexts_by_type) {}
|
||||||
|
|
||||||
|
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
||||||
|
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for delete query"});
|
||||||
|
}
|
||||||
|
|
||||||
|
query_builder_context ctx{schema_, contexts_by_type_};
|
||||||
|
delete_step_processor<ObjectType> processor{ctx};
|
||||||
|
|
||||||
|
const auto result = processor.build(ptr);
|
||||||
|
if (!result) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// relation inserts must run after all entity inserts were collected
|
||||||
|
for (auto &s : ctx.steps_) {
|
||||||
|
ctx.relation_steps_.push_back(std::move(s));
|
||||||
|
}
|
||||||
|
ctx.steps_.clear();
|
||||||
|
|
||||||
|
return utils::ok(std::move(ctx.relation_steps_));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const basic_schema &schema_;
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
|
};
|
||||||
|
} // namespace matador::query
|
||||||
|
|
||||||
|
#endif //MATADOR_DELETE_QUERY_BUILDER_HPP
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
#ifndef MATADOR_DELETE_STEP_HPP
|
||||||
|
#define MATADOR_DELETE_STEP_HPP
|
||||||
|
|
||||||
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/execute_step.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/internal/identifier_statement_binder.hpp"
|
||||||
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<typename ObjectType>
|
||||||
|
class delete_step_object final : public execute_step {
|
||||||
|
public:
|
||||||
|
delete_step_object(sql::query_context ctx, const object::object_ptr<ObjectType> &ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &/*conn*/) override {
|
||||||
|
id_ = ptr_.primary_key();
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
if (!ptr_) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
sql::identifier_statement_binder binder(stmt, 0);
|
||||||
|
binder.bind(id_);
|
||||||
|
|
||||||
|
if (const auto result = stmt.execute(); !result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache &cache, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
if (!ptr_) {
|
||||||
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.erase<ObjectType>(id_);
|
||||||
|
ptr_.change_state(object::object_state::Transient);
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ObjectType>
|
||||||
|
class delete_step_relation : public execute_step {
|
||||||
|
public:
|
||||||
|
delete_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
|
: execute_step(std::move(ctx))
|
||||||
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
|
stmt.bind(*ptr_);
|
||||||
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
|
return utils::failure(exec_result.err());
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
object::object_ptr<ObjectType> ptr_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //MATADOR_DELETE_STEP_HPP
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#ifndef MATADOR_EXECUTE_STEP_HPP
|
||||||
|
#define MATADOR_EXECUTE_STEP_HPP
|
||||||
|
|
||||||
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/primary_key_accessor.hpp"
|
||||||
|
#include "matador/utils/error.hpp"
|
||||||
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/query_context.hpp"
|
||||||
|
#include "matador/sql/executor.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
using resolver_service_ptr = std::shared_ptr<sql::resolver_service>;
|
||||||
|
|
||||||
|
class execute_step {
|
||||||
|
public:
|
||||||
|
explicit execute_step(sql::query_context ctx)
|
||||||
|
: ctx_(std::move(ctx)) {}
|
||||||
|
virtual ~execute_step() = default;
|
||||||
|
|
||||||
|
virtual utils::result<void, utils::error> prepare(sql::executor &conn) = 0;
|
||||||
|
virtual utils::result<void, utils::error> execute(sql::statement &stmt) = 0;
|
||||||
|
virtual utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] const sql::query_context& ctx() const { return ctx_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
utils::identifier id_;
|
||||||
|
utils::primary_key_accessor pk_accessor_;
|
||||||
|
sql::query_context ctx_;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_EXECUTE_STEP_HPP
|
||||||
@@ -19,7 +19,7 @@ public:
|
|||||||
void visit(const value_expression& node) override;
|
void visit(const value_expression& node) override;
|
||||||
void visit(const placeholder_expression& node) override;
|
void visit(const placeholder_expression& node) override;
|
||||||
|
|
||||||
const std::string& result() const;
|
[[nodiscard]] const std::string& result() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const sql::dialect &dialect_;
|
const sql::dialect &dialect_;
|
||||||
|
|||||||
@@ -12,14 +12,12 @@ class foreign_attributes;
|
|||||||
|
|
||||||
namespace matador::query::detail {
|
namespace matador::query::detail {
|
||||||
|
|
||||||
class fk_value_extractor
|
class fk_value_extractor {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
fk_value_extractor() = default;
|
fk_value_extractor() = default;
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
utils::database_type extract(Type &x)
|
utils::database_type extract(Type &x) {
|
||||||
{
|
|
||||||
access::process(*this, x);
|
access::process(*this, x);
|
||||||
return value_;
|
return value_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
#ifndef MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
#define MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "matador/object/collection.hpp"
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/query/basic_schema.hpp"
|
#include "matador/query/basic_schema.hpp"
|
||||||
#include "matador/query/intermediates/executable_query.hpp"
|
#include "matador/query/intermediates/executable_query.hpp"
|
||||||
@@ -11,6 +10,7 @@
|
|||||||
#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/query_builder_exception.hpp"
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
#include "matador/query/query_builder_utils.hpp"
|
||||||
|
|
||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
@@ -56,36 +56,57 @@ private:
|
|||||||
std::string join_column_;
|
std::string join_column_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class ObjectType>
|
template < typename ObjectType >
|
||||||
class insert_query_builder {
|
class insert_step_processor {
|
||||||
public:
|
public:
|
||||||
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
explicit insert_step_processor(query_builder_context &ctx)
|
||||||
: schema_(schema)
|
: ctx_{ctx}
|
||||||
, contexts_by_type_{contexts_by_type}
|
|
||||||
{}
|
{}
|
||||||
|
|
||||||
utils::result<std::vector<std::unique_ptr<insert_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
utils::result<void, utils::error> build(object::object_ptr<ObjectType> ptr, const bool as_relation_step = false) {
|
||||||
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
if (!ptr) {
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
||||||
}
|
}
|
||||||
|
|
||||||
steps_.clear();
|
|
||||||
visited_.clear();
|
|
||||||
|
|
||||||
ptr_ = ptr;
|
ptr_ = ptr;
|
||||||
const auto result = build_for(ptr, steps_);
|
|
||||||
|
const auto key = make_entity_visit_key<ObjectType>(*ptr_);
|
||||||
|
if (ctx_.visited_.find(key) != ctx_.visited_.end()) {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
ctx_.visited_.insert(key);
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(typeid(ObjectType));
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Traverse relations first => dependencies will be inserted before this object
|
||||||
|
try {
|
||||||
|
access::process(*this, *ptr_);
|
||||||
|
} catch (const query_builder_exception &ex) {
|
||||||
|
return utils::failure(ex.error());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Build INSERT for this object
|
||||||
|
const auto &info = it->second.node().info();
|
||||||
|
if (!info.has_primary_key() || it->second.pk_generator().type() == utils::generator_type::None) {
|
||||||
|
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
||||||
|
}
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto step = create_insert_step(cit->second.insert, it->second);
|
||||||
|
if (as_relation_step) {
|
||||||
|
ctx_.relation_steps_.push_back(std::move(step));
|
||||||
|
} else {
|
||||||
|
ctx_.steps_.push_back(std::move(step));
|
||||||
|
}
|
||||||
|
|
||||||
ptr_.reset();
|
ptr_.reset();
|
||||||
if (!result) {
|
|
||||||
return utils::failure(result.err());
|
|
||||||
}
|
|
||||||
|
|
||||||
// relation inserts must run after all entity inserts were collected
|
return utils::ok<void>();
|
||||||
for (auto &s : relation_steps_) {
|
|
||||||
steps_.push_back(std::move(s));
|
|
||||||
}
|
|
||||||
relation_steps_.clear();
|
|
||||||
|
|
||||||
return utils::ok(std::move(steps_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template < class PrimaryKeyType >
|
template < class PrimaryKeyType >
|
||||||
@@ -103,36 +124,51 @@ public:
|
|||||||
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
void on_has_one(const char * /*id*/, Pointer &obj, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||||
on_foreign_object(obj, attr);
|
on_foreign_object(obj, attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char * /*id*/,
|
void on_has_many(const char * /*id*/,
|
||||||
object::collection<object::object_ptr<CollectionType>> &objects,
|
object::collection<object::object_ptr<CollectionType>> &objects,
|
||||||
const char *join_column,
|
const char *join_column,
|
||||||
const utils::foreign_attributes &attr) {
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
has_many_linker<ObjectType> linker(ptr_, join_column);
|
has_many_linker<ObjectType> linker(ptr_, join_column);
|
||||||
|
insert_step_processor<CollectionType> processor{ctx_};
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
|
if (!obj) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (obj.is_transient()) {
|
if (obj.is_transient()) {
|
||||||
build_for(obj, relation_steps_);
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
access::process(linker, *obj);
|
access::process(linker, *obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class CollectionType>
|
template<class CollectionType>
|
||||||
void on_has_many(const char *id,
|
void on_has_many(const char *id,
|
||||||
object::collection<CollectionType> &objects,
|
object::collection<CollectionType> &objects,
|
||||||
const char *join_column,
|
const char *join_column,
|
||||||
const utils::foreign_attributes &attr) {
|
const utils::foreign_attributes &attr) {
|
||||||
|
if (id == nullptr || join_column == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto it = schema_.find(std::string{id});
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
if (it == schema_.end()) {
|
if (it == ctx_.schema_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type " + std::string{id});
|
throw query_builder_exception(error_code::UnknownType, "Unknown type " + std::string{id});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,204 +177,190 @@ public:
|
|||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
if (cit == contexts_by_type_.end()) {
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type" + std::string{id});
|
throw query_builder_exception(error_code::UnknownType, "Unknown type" + std::string{id});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
auto rel = object::make_object<relation_value_type>(join_column, "value", ptr_, obj);
|
auto rel = object::make_object<relation_value_type>(join_column, "value", ptr_, obj);
|
||||||
|
|
||||||
relation_steps_.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
ctx_.relation_steps_.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ForeignType>
|
template<class ForeignType>
|
||||||
void on_has_many_to_many(const char *id, object::collection<object::object_ptr<ForeignType>> &objects, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &attr) {
|
void on_has_many_to_many(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const char *join_column,
|
||||||
|
const char *inverse_join_column,
|
||||||
|
const utils::foreign_attributes &attr) {
|
||||||
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
if (id == nullptr || join_column == nullptr || inverse_join_column == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto it = schema_.find(std::string{id});
|
|
||||||
if (it == schema_.end()) {
|
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
|
||||||
}
|
|
||||||
|
|
||||||
using relation_value_type = object::many_to_many_relation<ObjectType, ForeignType>;
|
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()) {
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
}
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
|
id,
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
objects,
|
||||||
if (cit == contexts_by_type_.end()) {
|
attr,
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
}
|
return {std::string{relation_name}, local_type, foreign_type};
|
||||||
|
},
|
||||||
std::ignore = processing_many_to_many_relations_.insert(id);
|
[this, join_column, inverse_join_column](const auto &obj) {
|
||||||
std::vector<std::unique_ptr<insert_step>> insert_relation_steps;
|
return object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
||||||
for (auto &obj : objects) {
|
});
|
||||||
if (!obj) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure target exists as dependency (deps first)
|
|
||||||
if (obj.is_transient()) {
|
|
||||||
build_for(obj, relation_steps_);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rel = object::make_object<relation_value_type>(join_column, inverse_join_column, ptr_, obj);
|
|
||||||
|
|
||||||
access::process(*this, *rel);
|
|
||||||
|
|
||||||
insert_relation_steps.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
|
||||||
}
|
|
||||||
relation_steps_.insert(relation_steps_.end(), std::make_move_iterator(insert_relation_steps.begin()), std::make_move_iterator(insert_relation_steps.end()));
|
|
||||||
processing_many_to_many_relations_.erase(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ForeignType>
|
template<class ForeignType>
|
||||||
void on_has_many_to_many(const char *id, object::collection<object::object_ptr<ForeignType>> &objects, const utils::foreign_attributes &attr) {
|
void on_has_many_to_many(const char *id, object::collection<object::object_ptr<ForeignType>> &objects, const utils::foreign_attributes &attr) {
|
||||||
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
if (id == nullptr) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processing_many_to_many_relations_.find(id) != processing_many_to_many_relations_.end()) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
object::join_columns_collector collector;
|
object::join_columns_collector collector;
|
||||||
auto join_columns = collector.collect<ForeignType>();
|
auto join_columns = collector.collect<ForeignType>();
|
||||||
|
if (join_columns.join_column.empty() || join_columns.inverse_join_column.empty()) {
|
||||||
const auto it = schema_.find(std::string{id});
|
return;
|
||||||
if (it == schema_.end()) {
|
|
||||||
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<ForeignType, ObjectType>;
|
||||||
|
const std::type_index foreign_type{typeid(ForeignType)};
|
||||||
if (std::type_index(typeid(relation_value_type)) != it->second.node().info().type_index()) {
|
const std::type_index local_type{typeid(ObjectType)};
|
||||||
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type");
|
insert_many_to_many_relations<relation_value_type>(
|
||||||
|
id,
|
||||||
|
objects,
|
||||||
|
attr,
|
||||||
|
[foreign_type, local_type](const char* relation_name) -> processing_many_to_many_key {
|
||||||
|
return {std::string{relation_name}, foreign_type, local_type};
|
||||||
|
},
|
||||||
|
[this, join_columns = std::move(join_columns)](const auto &obj) {
|
||||||
|
return object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
private:
|
||||||
if (cit == contexts_by_type_.end()) {
|
template<class PointerType>
|
||||||
throw query_builder_exception(error_code::UnknownType, "Unknown type");
|
void on_foreign_object(object::object_ptr<PointerType> &obj, const utils::foreign_attributes &attr) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert) || !obj || !obj.is_transient()) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ignore = processing_many_to_many_relations_.insert(id);
|
insert_step_processor<PointerType> processor{ctx_};
|
||||||
std::vector<std::unique_ptr<insert_step>> insert_relation_steps;
|
|
||||||
|
auto result = processor.build(obj);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class LocalType, class ForeignType, class RelationKeyFactory, class RelationFactory>
|
||||||
|
void insert_many_to_many_relations(const char *id,
|
||||||
|
object::collection<object::object_ptr<ForeignType>> &objects,
|
||||||
|
const utils::foreign_attributes &attr,
|
||||||
|
RelationKeyFactory make_relation_key,
|
||||||
|
RelationFactory make_relation) {
|
||||||
|
if (!utils::is_cascade_type_set(attr.cascade(), utils::cascade_type::Insert)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto key = make_relation_key(id);
|
||||||
|
if (ctx_.processing_many_to_many_relations_.find(key) != ctx_.processing_many_to_many_relations_.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto it = ctx_.schema_.find(std::string{id});
|
||||||
|
if (it == ctx_.schema_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "Unknown type for relation " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::type_index(typeid(LocalType)) != it->second.node().info().type_index()) {
|
||||||
|
throw query_builder_exception(error_code::InvalidRelationType, "Invalid relation type for " + std::string{id});
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cit = ctx_.contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
if (cit == ctx_.contexts_by_type_.end()) {
|
||||||
|
throw query_builder_exception(error_code::UnknownType, "No query contexts for type " + it->second.node().name());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ignore = ctx_.processing_many_to_many_relations_.insert(key);
|
||||||
|
std::vector<std::unique_ptr<execute_step>> insert_relation_steps;
|
||||||
|
insert_step_processor<ForeignType> processor(ctx_);
|
||||||
for (auto &obj : objects) {
|
for (auto &obj : objects) {
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure target exists as dependency (deps first)
|
|
||||||
if (obj.is_transient()) {
|
if (obj.is_transient()) {
|
||||||
build_for(obj, relation_steps_);
|
auto result = processor.build(obj, true);
|
||||||
|
if (!result) {
|
||||||
|
throw query_builder_exception(result.release_error());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto rel = object::make_object<relation_value_type>(join_columns.inverse_join_column, join_columns.join_column, obj, ptr_);
|
auto rel = make_relation(obj);
|
||||||
|
|
||||||
access::process(*this, *rel);
|
// access::process(*this, *rel);
|
||||||
|
|
||||||
insert_relation_steps.push_back(std::make_unique<insert_step_relation<relation_value_type>>(cit->second.insert, rel));
|
insert_relation_steps.push_back(std::make_unique<insert_step_relation<LocalType>>(cit->second.insert, rel));
|
||||||
}
|
}
|
||||||
relation_steps_.insert(relation_steps_.end(), std::make_move_iterator(insert_relation_steps.begin()), std::make_move_iterator(insert_relation_steps.end()));
|
ctx_.relation_steps_.insert(
|
||||||
processing_many_to_many_relations_.erase(id);
|
ctx_.relation_steps_.end(),
|
||||||
|
std::make_move_iterator(insert_relation_steps.begin()),
|
||||||
|
std::make_move_iterator(insert_relation_steps.end()));
|
||||||
|
ctx_.processing_many_to_many_relations_.erase(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<execute_step> create_insert_step(const sql::query_context& query_ctx, const schema_node& node) {
|
||||||
|
if (node.pk_generator().type() == utils::generator_type::Manual) {
|
||||||
|
return std::make_unique<insert_step_pk_manual<ObjectType>>(query_ctx, ptr_);
|
||||||
|
}
|
||||||
|
if (node.pk_generator().type() == utils::generator_type::Identity) {
|
||||||
|
return std::make_unique<insert_step_pk_identity<ObjectType>>(query_ctx, ptr_, node.node().info().primary_key_attribute()->name());
|
||||||
|
}
|
||||||
|
return std::make_unique<insert_step_pk_generated<ObjectType>>(query_ctx, ptr_, node.pk_generator());
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<class EntityType>
|
query_builder_context& ctx_;
|
||||||
static std::pair<std::type_index, const void *> make_visit_key(const object::object_ptr<EntityType> &ptr) {
|
object::object_ptr<ObjectType> ptr_;
|
||||||
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&(*ptr))};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct visit_key_hash {
|
|
||||||
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
|
|
||||||
// combine hashes (simple + sufficient here)
|
|
||||||
const size_t h1 = p.first.hash_code();
|
|
||||||
const size_t h2 = std::hash<const void *>{}(p.second);
|
|
||||||
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class EntityType>
|
template<class ObjectType>
|
||||||
utils::result<void, utils::error> build_for(const object::object_ptr<EntityType> &ptr, std::vector<std::unique_ptr<insert_step>> &steps) {
|
class insert_query_builder {
|
||||||
if (!ptr) {
|
public:
|
||||||
return utils::failure(utils::error{error_code::InvalidObject, "Object is null"});
|
explicit insert_query_builder(const basic_schema &schema, const std::unordered_map<std::type_index, query_contexts> &contexts_by_type)
|
||||||
|
: schema_(schema)
|
||||||
|
, contexts_by_type_(contexts_by_type)
|
||||||
|
{}
|
||||||
|
|
||||||
|
utils::result<std::vector<std::unique_ptr<execute_step>>, utils::error> build(const object::object_ptr<ObjectType> &ptr) {
|
||||||
|
if (const auto it = schema_.find(typeid(ObjectType)); it == schema_.end()) {
|
||||||
|
return utils::failure(utils::error{error_code::UnknownType, "Unknown type for insert query"});
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto key = make_visit_key<EntityType>(ptr);
|
query_builder_context ctx{schema_, contexts_by_type_};
|
||||||
if (visited_.find(key) != visited_.end()) {
|
insert_step_processor<ObjectType> processor{ctx};
|
||||||
return utils::ok<void>();
|
|
||||||
}
|
|
||||||
visited_.insert(key);
|
|
||||||
|
|
||||||
const auto it = schema_.find(typeid(EntityType));
|
const auto result = processor.build(ptr);
|
||||||
if (it == schema_.end()) {
|
if (!result) {
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) Traverse relations first => dependencies will be inserted before this object
|
// relation inserts must run after all entity inserts were collected
|
||||||
try {
|
for (auto &s : ctx.relation_steps_) {
|
||||||
access::process(*this, *ptr);
|
ctx.steps_.push_back(std::move(s));
|
||||||
} catch (const query_builder_exception &ex) {
|
}
|
||||||
return utils::failure(ex.error());
|
ctx.relation_steps_.clear();
|
||||||
|
|
||||||
|
return utils::ok(std::move(ctx.steps_));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Build INSERT for this object
|
|
||||||
const auto &info = it->second.node().info();
|
|
||||||
if (!info.has_primary_key() || it->second.pk_generator().type() == utils::generator_type::None) {
|
|
||||||
return utils::failure(utils::error{error_code::MissingPrimaryKey, "Type " + info.name() + " has no primary key"});
|
|
||||||
}
|
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
|
||||||
if (cit == contexts_by_type_.end()) {
|
|
||||||
return utils::failure(utils::error{error_code::UnknownType, "Unknown type"});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (it->second.pk_generator().type() == utils::generator_type::Manual) {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_manual<EntityType>>(cit->second.insert, ptr));
|
|
||||||
} else if (it->second.pk_generator().type() == utils::generator_type::Identity) {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_identity<EntityType>>(cit->second.insert, ptr, info.primary_key_attribute()->name()));
|
|
||||||
} else {
|
|
||||||
steps.push_back(std::make_unique<insert_step_pk_generated<EntityType>>(cit->second.insert, ptr, it->second.pk_generator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils::ok<void>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class Pointer>
|
|
||||||
void on_foreign_object(Pointer &obj, const utils::foreign_attributes & /*attr*/) {
|
|
||||||
if (!obj) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dependency only matters if the referenced object must be inserted
|
|
||||||
if (obj.is_persistent()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
using dep_t = std::remove_reference_t<decltype(*obj)>;
|
|
||||||
build_for<dep_t>(obj, steps_);
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
const basic_schema &schema_;
|
const basic_schema &schema_;
|
||||||
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
|
|
||||||
object::object_ptr<ObjectType> ptr_;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<insert_step>> steps_;
|
|
||||||
std::vector<std::unique_ptr<insert_step>> relation_steps_;
|
|
||||||
std::unordered_set<std::pair<std::type_index, const void *>, visit_key_hash> visited_;
|
|
||||||
std::unordered_set<std::string> processing_many_to_many_relations_;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
#endif //MATADOR_INSERT_QUERY_BUILDER_HPP
|
||||||
@@ -1,43 +1,49 @@
|
|||||||
#ifndef MATADOR_INSERT_STEP_HPP
|
#ifndef MATADOR_INSERT_STEP_HPP
|
||||||
#define MATADOR_INSERT_STEP_HPP
|
#define MATADOR_INSERT_STEP_HPP
|
||||||
|
|
||||||
#include <utility>
|
#include "matador/query/abstract_pk_generator.hpp"
|
||||||
|
#include "matador/query/execute_step.hpp"
|
||||||
#include "matador/utils/primary_key_accessor.hpp"
|
#include "matador/query/error_code.hpp"
|
||||||
#include "matador/utils/error.hpp"
|
|
||||||
#include "matador/utils/result.hpp"
|
|
||||||
|
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
#include "matador/object/object_ptr.hpp"
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include "matador/sql/query_context.hpp"
|
|
||||||
#include "matador/sql/executor.hpp"
|
|
||||||
#include "matador/sql/execute_result.hpp"
|
#include "matador/sql/execute_result.hpp"
|
||||||
|
#include "matador/sql/resolver_service.hpp"
|
||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
|
|
||||||
#include "matador/query/error_code.hpp"
|
|
||||||
#include "matador/query/abstract_pk_generator.hpp"
|
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
class insert_step {
|
template<typename ObjectType>
|
||||||
public:
|
utils::result<void, utils::error> finalize_inserted_object(object::object_ptr<ObjectType> &ptr,
|
||||||
explicit insert_step(sql::query_context ctx)
|
const utils::identifier& pk,
|
||||||
: ctx_(std::move(ctx)) {}
|
object::object_cache &cache,
|
||||||
virtual ~insert_step() = default;
|
const std::shared_ptr<sql::resolver_service> &resolver_service) {
|
||||||
|
if (!ptr) {
|
||||||
|
return utils::failure(utils::error(error_code::InvalidObject, "Inserted object is null."));
|
||||||
|
}
|
||||||
|
|
||||||
virtual utils::result<void, utils::error> prepare(sql::executor &conn) = 0;
|
if (!resolver_service) {
|
||||||
virtual utils::result<void, utils::error> insert(sql::statement &stmt) = 0;
|
return utils::failure(utils::error(error_code::UnknownType, "Missing resolver service."));
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] const sql::query_context& ctx() const { return ctx_; }
|
auto resolver = resolver_service->template object_resolver<ObjectType>();
|
||||||
protected:
|
if (!resolver) {
|
||||||
utils::primary_key_accessor pk_accessor_;
|
return utils::failure(utils::error(error_code::UnknownType, "Missing object resolver for inserted type."));
|
||||||
sql::query_context ctx_;
|
}
|
||||||
};
|
|
||||||
|
if (!cache.import<ObjectType>(pk, ptr.proxy(), resolver)) {
|
||||||
|
return utils::failure(utils::error(error_code::FailedToFindObject, "Object cache already contains another live proxy for inserted object."));
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr.change_state(object::object_state::Persistent);
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_generated : public insert_step {
|
class insert_step_pk_generated : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_generated(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, abstract_pk_generator& pk_generator)
|
insert_step_pk_generated(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, abstract_pk_generator& pk_generator)
|
||||||
: insert_step(std::move(ctx))
|
: execute_step(std::move(ctx))
|
||||||
, ptr_(ptr)
|
, ptr_(ptr)
|
||||||
, pk_generator_(pk_generator){}
|
, pk_generator_(pk_generator){}
|
||||||
|
|
||||||
@@ -46,12 +52,13 @@ public:
|
|||||||
if (!result.is_ok()) {
|
if (!result.is_ok()) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
pk_accessor_.set(*ptr_, utils::identifier{*result});
|
id_ = *result;
|
||||||
|
pk_accessor_.set(*ptr_, id_);
|
||||||
|
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<void, utils::error> insert(sql::statement& stmt) override {
|
utils::result<void, utils::error> execute(sql::statement& stmt) override {
|
||||||
stmt.bind(*ptr_);
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
@@ -62,16 +69,20 @@ public:
|
|||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
object::object_ptr<ObjectType> ptr_;
|
object::object_ptr<ObjectType> ptr_;
|
||||||
abstract_pk_generator& pk_generator_;
|
abstract_pk_generator& pk_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_identity : public insert_step {
|
class insert_step_pk_identity : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_identity(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, std::string pk_column_name)
|
insert_step_pk_identity(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr, std::string pk_column_name)
|
||||||
: insert_step(std::move(ctx))
|
: execute_step(std::move(ctx))
|
||||||
, ptr_(ptr)
|
, ptr_(ptr)
|
||||||
, pk_column_name_(std::move(pk_column_name)){}
|
, pk_column_name_(std::move(pk_column_name)){}
|
||||||
|
|
||||||
@@ -79,7 +90,7 @@ public:
|
|||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<void, utils::error> insert(sql::statement& stmt) override {
|
utils::result<void, utils::error> execute(sql::statement& stmt) override {
|
||||||
stmt.bind(*ptr_);
|
stmt.bind(*ptr_);
|
||||||
|
|
||||||
auto result = stmt.fetch_one();
|
auto result = stmt.fetch_one();
|
||||||
@@ -92,56 +103,64 @@ public:
|
|||||||
|
|
||||||
auto rec = result->value();
|
auto rec = result->value();
|
||||||
const auto& f = rec.at(pk_column_name_);
|
const auto& f = rec.at(pk_column_name_);
|
||||||
utils::identifier id;
|
if (auto res = id_.assign(f.value()); !res.is_ok()) {
|
||||||
if (auto res = id.assign(f.value()); !res.is_ok()) {
|
|
||||||
return utils::failure(res.err());
|
return utils::failure(res.err());
|
||||||
}
|
}
|
||||||
pk_accessor_.set(*ptr_, id);
|
pk_accessor_.set(*ptr_, id_);
|
||||||
|
|
||||||
ptr_.change_state(object::object_state::Persistent);
|
ptr_.change_state(object::object_state::Persistent);
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
object::object_ptr<ObjectType> ptr_;
|
object::object_ptr<ObjectType> ptr_;
|
||||||
std::string pk_column_name_;
|
std::string pk_column_name_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_pk_manual : public insert_step {
|
class insert_step_pk_manual : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_pk_manual(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
insert_step_pk_manual(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
: insert_step(std::move(ctx))
|
: execute_step(std::move(ctx))
|
||||||
, ptr_(ptr) {}
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
utils::result<void, utils::error> prepare(sql::executor &) override {
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
utils::result<void, utils::error> insert(sql::statement &stmt) override {
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
stmt.bind(*ptr_);
|
stmt.bind(*ptr_);
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
return utils::failure(exec_result.err());
|
return utils::failure(exec_result.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
id_ = object::primary_key_resolver::resolve_object(*ptr_).pk;
|
||||||
ptr_.change_state(object::object_state::Persistent);
|
ptr_.change_state(object::object_state::Persistent);
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& cache, const resolver_service_ptr& resolver_service) override {
|
||||||
|
return finalize_inserted_object(ptr_, id_, cache, resolver_service);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
object::object_ptr<ObjectType> ptr_;
|
object::object_ptr<ObjectType> ptr_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename ObjectType>
|
template <typename ObjectType>
|
||||||
class insert_step_relation : public insert_step {
|
class insert_step_relation : public execute_step {
|
||||||
public:
|
public:
|
||||||
insert_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
insert_step_relation(sql::query_context ctx, const object::object_ptr<ObjectType>& ptr)
|
||||||
: insert_step(std::move(ctx))
|
: execute_step(std::move(ctx))
|
||||||
, ptr_(ptr) {}
|
, ptr_(ptr) {}
|
||||||
|
|
||||||
utils::result<void, utils::error> prepare(sql::executor &) override {
|
utils::result<void, utils::error> prepare(sql::executor &) override {
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
utils::result<void, utils::error> insert(sql::statement &stmt) override {
|
utils::result<void, utils::error> execute(sql::statement &stmt) override {
|
||||||
stmt.bind(*ptr_);
|
stmt.bind(*ptr_);
|
||||||
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
if (const auto exec_result = stmt.execute(); !exec_result.is_ok()) {
|
||||||
return utils::failure(exec_result.err());
|
return utils::failure(exec_result.err());
|
||||||
@@ -150,6 +169,10 @@ public:
|
|||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::result<void, utils::error> finalize(object::object_cache& /*cache*/, const resolver_service_ptr& /*resolver_service*/) override {
|
||||||
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
object::object_ptr<ObjectType> ptr_;
|
object::object_ptr<ObjectType> ptr_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public:
|
|||||||
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::execute_result, utils::error> execute(const sql::executor &exec) const;
|
||||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
||||||
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
||||||
[[nodiscard]] std::string str(const sql::executor &exec) const;
|
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,6 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(sql::executor &exec) const;
|
||||||
|
|
||||||
[[nodiscard]] std::string str(const sql::executor &exec) const;
|
|
||||||
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
[[nodiscard]] std::string str(const sql::dialect &d) const;
|
||||||
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
[[nodiscard]] sql::query_context compile(const sql::dialect &d) const;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,5 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] query_alter_table_intermediate table(const table &tab) const;
|
[[nodiscard]] query_alter_table_intermediate table(const table &tab) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif //MATADOR_QUERY_ALTER_INTERMEDIATE_HPP
|
#endif //MATADOR_QUERY_ALTER_INTERMEDIATE_HPP
|
||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
using query_intermediate::query_intermediate;
|
using query_intermediate::query_intermediate;
|
||||||
|
|
||||||
query_create_table_columns_intermediate columns(std::initializer_list<object::attribute> attributes);
|
query_create_table_columns_intermediate columns(std::initializer_list<object::attribute> attributes);
|
||||||
query_create_table_columns_intermediate columns(const std::list<object::attribute> &attributes);
|
query_create_table_columns_intermediate columns(const std::vector<object::attribute> &attributes);
|
||||||
query_create_table_columns_intermediate columns(std::initializer_list<table_column> columns);
|
query_create_table_columns_intermediate columns(std::initializer_list<table_column> columns);
|
||||||
query_create_table_columns_intermediate columns(const std::list<table_column> &columns);
|
query_create_table_columns_intermediate columns(const std::list<table_column> &columns);
|
||||||
query_create_table_columns_intermediate columns(const std::vector<table_column> &columns);
|
query_create_table_columns_intermediate columns(const std::vector<table_column> &columns);
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
#ifndef QUERY_INTERMEDIATE_HPP
|
#ifndef QUERY_INTERMEDIATE_HPP
|
||||||
#define QUERY_INTERMEDIATE_HPP
|
#define QUERY_INTERMEDIATE_HPP
|
||||||
|
|
||||||
// #include "matador/query/query_data.hpp"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
|||||||
@@ -12,10 +12,6 @@ namespace matador::query::internal {
|
|||||||
class column_value_pair {
|
class column_value_pair {
|
||||||
public:
|
public:
|
||||||
column_value_pair() = default;
|
column_value_pair() = default;
|
||||||
// column_value_pair(table_column col, utils::database_type value);
|
|
||||||
// column_value_pair(const std::string& name, utils::database_type value);
|
|
||||||
// column_value_pair(const char *name, utils::database_type value);
|
|
||||||
// column_value_pair(const char *name, utils::placeholder p);
|
|
||||||
column_value_pair(column_value_pair&& x) = default;
|
column_value_pair(column_value_pair&& x) = default;
|
||||||
column_value_pair& operator=(column_value_pair&& x) = default;
|
column_value_pair& operator=(column_value_pair&& x) = default;
|
||||||
column_value_pair(table_column col, column_expression_ptr expression);
|
column_value_pair(table_column col, column_expression_ptr expression);
|
||||||
@@ -24,12 +20,10 @@ public:
|
|||||||
friend bool operator!=(const column_value_pair &lhs, const column_value_pair &rhs);
|
friend bool operator!=(const column_value_pair &lhs, const column_value_pair &rhs);
|
||||||
|
|
||||||
[[nodiscard]] const table_column& col() const;
|
[[nodiscard]] const table_column& col() const;
|
||||||
// [[nodiscard]] const std::variant<utils::placeholder, utils::database_type>& value() const;
|
|
||||||
[[nodiscard]] const abstract_column_expression& expression() const;
|
[[nodiscard]] const abstract_column_expression& expression() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
table_column column_;
|
table_column column_;
|
||||||
// std::variant<utils::placeholder, utils::database_type> value_;
|
|
||||||
column_expression_ptr expression_;
|
column_expression_ptr expression_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "matador/object/restriction.hpp"
|
|
||||||
|
|
||||||
namespace matador::query::internal {
|
namespace matador::query::internal {
|
||||||
|
|
||||||
class query_alter_part final : public query_part {
|
class query_alter_part final : public query_part {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ TABLE_NAME##_table()\
|
|||||||
: TABLE_NAME##_table(#TABLE_NAME) \
|
: TABLE_NAME##_table(#TABLE_NAME) \
|
||||||
{} \
|
{} \
|
||||||
TABLE_NAME##_table(const std::string& alias) \
|
TABLE_NAME##_table(const std::string& alias) \
|
||||||
: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}) \
|
: typed_table(#TABLE_NAME, alias, {MAP(FIELD_STRING, __VA_ARGS__)}, {}, {}) \
|
||||||
MAP(INIT_FIELD, __VA_ARGS__) \
|
MAP(INIT_FIELD, __VA_ARGS__) \
|
||||||
{} \
|
{} \
|
||||||
MAP(FIELD, __VA_ARGS__) \
|
MAP(FIELD, __VA_ARGS__) \
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "matador/query/query_data.hpp"
|
#include "matador/query/query_data.hpp"
|
||||||
|
|
||||||
#include "matador/sql/query_context.hpp"
|
#include "matador/sql/query_context.hpp"
|
||||||
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
#include "matador/utils/placeholder.hpp"
|
#include "matador/utils/placeholder.hpp"
|
||||||
|
|
||||||
@@ -12,7 +13,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
class connection_impl;
|
|
||||||
class dialect;
|
class dialect;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,6 @@ public:
|
|||||||
const sql::dialect &d,
|
const sql::dialect &d,
|
||||||
std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
std::optional<std::reference_wrapper<const sql::connection_impl>> conn);
|
||||||
|
|
||||||
protected:
|
|
||||||
void visit(internal::query_alter_part& part) override;
|
void visit(internal::query_alter_part& part) override;
|
||||||
void visit(internal::query_alter_table_part& part) override;
|
void visit(internal::query_alter_table_part& part) override;
|
||||||
void visit(internal::query_add_key_constraint_part& part) override;
|
void visit(internal::query_add_key_constraint_part& part) override;
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ namespace matador::query {
|
|||||||
|
|
||||||
class query_builder_exception final : public std::exception {
|
class query_builder_exception final : public std::exception {
|
||||||
public:
|
public:
|
||||||
explicit query_builder_exception(error_code error, std::string &&msg);
|
explicit query_builder_exception(utils::error &&err);
|
||||||
|
query_builder_exception(error_code error, std::string &&msg);
|
||||||
|
|
||||||
[[nodiscard]] const utils::error &error() const;
|
[[nodiscard]] const utils::error &error() const;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#ifndef MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
|
#define MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <typeindex>
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
template<class EntityType>
|
||||||
|
static std::pair<std::type_index, const void *> make_entity_visit_key(const EntityType &ptr) {
|
||||||
|
return {std::type_index(typeid(EntityType)), static_cast<const void *>(&ptr)};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct entity_visit_key_hash {
|
||||||
|
size_t operator()(const std::pair<std::type_index, const void *> &p) const noexcept {
|
||||||
|
const size_t h1 = p.first.hash_code();
|
||||||
|
const size_t h2 = std::hash<const void *>{}(p.second);
|
||||||
|
return h1 ^ (h2 + 0x9e3779b97f4a7c15ULL + (h1 << 6) + (h1 >> 2));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct processing_many_to_many_key {
|
||||||
|
std::string id;
|
||||||
|
std::type_index local_type{typeid(void)};
|
||||||
|
std::type_index foreign_type{typeid(void)};
|
||||||
|
bool operator==(processing_many_to_many_key const &other) const {
|
||||||
|
return local_type == other.local_type && foreign_type == other.foreign_type && id == other.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class LocalType, typename ForeignType>
|
||||||
|
static processing_many_to_many_key make_processing_many_to_many_key(const std::string &id) {
|
||||||
|
return {id, std::type_index(typeid(LocalType)), std::type_index(typeid(ForeignType))};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct processing_many_to_many_key_hash {
|
||||||
|
size_t operator()(const processing_many_to_many_key &p) const noexcept {
|
||||||
|
size_t seed = std::hash<std::type_index>{}(p.local_type);
|
||||||
|
|
||||||
|
const size_t foreign_hash = std::hash<std::type_index>{}(p.foreign_type);
|
||||||
|
seed ^= foreign_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||||
|
|
||||||
|
const size_t id_hash = std::hash<std::string>{}(p.id);
|
||||||
|
seed ^= id_hash + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2);
|
||||||
|
|
||||||
|
return seed;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct query_builder_context {
|
||||||
|
const basic_schema &schema_;
|
||||||
|
const std::unordered_map<std::type_index, query_contexts> &contexts_by_type_;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<execute_step>> steps_{};
|
||||||
|
std::vector<std::unique_ptr<execute_step>> relation_steps_{};
|
||||||
|
std::unordered_set<std::pair<std::type_index, const void *>, entity_visit_key_hash> visited_{};
|
||||||
|
std::unordered_set<processing_many_to_many_key, processing_many_to_many_key_hash> processing_many_to_many_relations_{};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif //MATADOR_QUERY_BUILDER_UTILS_HPP
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
#ifndef MATADOR_QUERY_UTILS_HPP
|
#ifndef MATADOR_QUERY_UTILS_HPP
|
||||||
#define MATADOR_QUERY_UTILS_HPP
|
#define MATADOR_QUERY_UTILS_HPP
|
||||||
|
|
||||||
#include "table.hpp"
|
#include "matador/utils/value.hpp"
|
||||||
#include "matador/sql/dialect.hpp"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -11,6 +10,7 @@ class dialect;
|
|||||||
struct query_context;
|
struct query_context;
|
||||||
}
|
}
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
class table;
|
||||||
class table_column;
|
class table_column;
|
||||||
|
|
||||||
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col);
|
void prepare_column(sql::query_context& ctx, const sql::dialect& d, const table_column& col);
|
||||||
|
|||||||
@@ -318,6 +318,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
iterator insert_table(const std::type_index& ti, const object::repository_node &node, utils::generator_type generator_type);
|
iterator insert_table(const std::type_index& ti, const object::repository_node &node, utils::generator_type generator_type);
|
||||||
|
iterator insert_relation_table(const std::type_index& ti, const object::repository_node &node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
@@ -352,10 +353,6 @@ utils::result<sql::query_context, utils::error> query_object_resolver_producer<T
|
|||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<sql::query_context, utils::error> query_joined_object_resolver_producer<Type>::build_query(const sql::dialect &d) {
|
utils::result<sql::query_context, utils::error> query_joined_object_resolver_producer<Type>::build_query(const sql::dialect &d) {
|
||||||
// producer_creator pc(repo_, typeid(Type));
|
|
||||||
// Type obj;
|
|
||||||
// access::process(pc, obj);
|
|
||||||
|
|
||||||
select_query_builder qb(repo_);
|
select_query_builder qb(repo_);
|
||||||
const auto *join_column = table_[collection_name()];
|
const auto *join_column = table_[collection_name()];
|
||||||
const auto result = qb.build<Type>(*join_column == utils::_);
|
const auto result = qb.build<Type>(*join_column == utils::_);
|
||||||
@@ -368,17 +365,18 @@ utils::result<sql::query_context, utils::error> query_joined_object_resolver_pro
|
|||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
void schema_observer<Type>::on_attach(const object::repository_node &node, const Type &/*prototype*/) const {
|
void schema_observer<Type>::on_attach(const object::repository_node &node, const Type &/*prototype*/) const {
|
||||||
|
const object::object_info<Type>& info = node.info<Type>().get();
|
||||||
|
if (info.has_primary_key()) {
|
||||||
primary_key_generator_finder finder;
|
primary_key_generator_finder finder;
|
||||||
const auto generator_type = finder.find(node.info<Type>().get());
|
const auto generator_type = finder.find(info);
|
||||||
|
|
||||||
const auto it = schema_.insert_table(typeid(Type), node, generator_type);
|
const auto it = schema_.insert_table(typeid(Type), node, generator_type);
|
||||||
|
|
||||||
if (!it->second.node().info().has_primary_key()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto producer = std::make_unique<query_object_resolver_producer<Type>>(schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name());
|
auto producer = std::make_unique<query_object_resolver_producer<Type>>(schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name());
|
||||||
schema_.resolver_producers_[typeid(Type)] = std::move(producer);
|
schema_.resolver_producers_[typeid(Type)] = std::move(producer);
|
||||||
|
} else {
|
||||||
|
const auto it = schema_.insert_relation_table(typeid(Type), node);
|
||||||
|
// auto producer = std::make_unique<query_object_resolver_producer<Type>>(schema_, it->second.table(), it->second.node().info().primary_key_attribute()->name());
|
||||||
|
// schema_.resolver_producers_[typeid(Type)] = std::move(producer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Type>
|
template <typename Type>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef MATADOR_SCHEMA_UTILS_HPP
|
||||||
|
#define MATADOR_SCHEMA_UTILS_HPP
|
||||||
|
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
|
#include "matador/query/basic_schema.hpp"
|
||||||
|
|
||||||
|
namespace matador::sql {
|
||||||
|
class dialect;
|
||||||
|
}
|
||||||
|
namespace matador::query {
|
||||||
|
query_contexts to_query_contexts(const schema_node &node, const sql::dialect &d);
|
||||||
|
}
|
||||||
|
#endif //MATADOR_SCHEMA_UTILS_HPP
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#define QUERY_SESSION_HPP
|
#define QUERY_SESSION_HPP
|
||||||
|
|
||||||
#include "matador/query/error_code.hpp"
|
#include "matador/query/error_code.hpp"
|
||||||
|
#include "matador/query/delete_query_builder.hpp"
|
||||||
#include "matador/query/select_query_builder.hpp"
|
#include "matador/query/select_query_builder.hpp"
|
||||||
#include "matador/query/criteria.hpp"
|
#include "matador/query/criteria.hpp"
|
||||||
#include "matador/query/insert_query_builder.hpp"
|
#include "matador/query/insert_query_builder.hpp"
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
#include "matador/sql/statement.hpp"
|
#include "matador/sql/statement.hpp"
|
||||||
#include "matador/sql/statement_cache.hpp"
|
#include "matador/sql/statement_cache.hpp"
|
||||||
|
|
||||||
|
#include "matador/object/object_cache.hpp"
|
||||||
#include "matador/object/object_ptr.hpp"
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -51,7 +53,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
@@ -69,6 +70,8 @@ private:
|
|||||||
mutable sql::statement_cache cache_;
|
mutable sql::statement_cache cache_;
|
||||||
const sql::dialect &dialect_;
|
const sql::dialect &dialect_;
|
||||||
|
|
||||||
|
object::object_cache object_cache_;
|
||||||
|
|
||||||
const 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_;
|
||||||
@@ -109,12 +112,18 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(object::ob
|
|||||||
return utils::failure(stmt.err());
|
return utils::failure(stmt.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (const auto result = step->insert(*stmt); !result.is_ok()) {
|
if (const auto result = step->execute(*stmt); !result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After successfully executed all inserts, add them to the object cache
|
||||||
|
for (auto &step : *steps) {
|
||||||
|
if (const auto result = step->finalize(object_cache_, resolver_service_); !result.is_ok()) {
|
||||||
return utils::failure(result.err());
|
return utils::failure(result.err());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
obj.change_state(object::object_state::Persistent);
|
|
||||||
return utils::ok(obj);
|
return utils::ok(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,28 +207,67 @@ utils::result<object::object_ptr<Type>, utils::error> session::update(const obje
|
|||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
||||||
const auto it = schema_.find(typeid(Type));
|
if (!obj.is_persistent()) {
|
||||||
if (it == schema_.end()) {
|
return utils::ok<void>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto it = schema_.find(typeid(Type)); 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."));
|
||||||
}
|
}
|
||||||
using namespace matador::utils;
|
|
||||||
using namespace matador::query;
|
|
||||||
|
|
||||||
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
delete_query_builder<Type> dqb(schema_, contexts_by_type_);
|
||||||
const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
auto steps = dqb.build(obj);
|
||||||
if (cit == contexts_by_type_.end()) {
|
if (!steps.is_ok()) {
|
||||||
return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build delete dependency queries."));
|
||||||
}
|
}
|
||||||
auto stmt = cache_.acquire(cit->second.delete_one);
|
|
||||||
|
for (auto &step : *steps) {
|
||||||
|
const auto conn = pool_.acquire();
|
||||||
|
if (!conn.valid()) {
|
||||||
|
return utils::failure(make_error(error_code::FailedToAcquirePool, "Failed to acquire connection pool for primary key generation."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const auto result = step->prepare(*conn); !result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
conn.release();
|
||||||
|
|
||||||
|
auto stmt = cache_.acquire(step->ctx());
|
||||||
if (!stmt.is_ok()) {
|
if (!stmt.is_ok()) {
|
||||||
return failure(stmt.err());
|
return utils::failure(stmt.err());
|
||||||
}
|
}
|
||||||
|
|
||||||
pk_object_binder binder(*stmt, stmt->bind_pos());
|
if (const auto result = step->execute(*stmt); !result.is_ok()) {
|
||||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
return utils::failure(result.err());
|
||||||
return utils::failure(update_result.err());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// After successfully executed all deletes, add them to the object cache
|
||||||
|
for (auto &step : *steps) {
|
||||||
|
if (const auto result = step->finalize(object_cache_, resolver_service_); !result.is_ok()) {
|
||||||
|
return utils::failure(result.err());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
|
// using namespace matador::utils;
|
||||||
|
// using namespace matador::query;
|
||||||
|
//
|
||||||
|
// const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
||||||
|
// const auto cit = contexts_by_type_.find(it->second.node().info().type_index());
|
||||||
|
// if (cit == contexts_by_type_.end()) {
|
||||||
|
// return failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||||
|
// }
|
||||||
|
// auto stmt = cache_.acquire(cit->second.delete_one);
|
||||||
|
// if (!stmt.is_ok()) {
|
||||||
|
// return failure(stmt.err());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// pk_object_binder binder(*stmt, stmt->bind_pos());
|
||||||
|
// if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
||||||
|
// return utils::failure(update_result.err());
|
||||||
|
// }
|
||||||
|
// return utils::ok<void>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Type, typename PrimaryKeyType>
|
template<typename Type, typename PrimaryKeyType>
|
||||||
@@ -233,6 +281,15 @@ utils::result<object::object_ptr<Type>, utils::error> session::find(const Primar
|
|||||||
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."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto resolver = resolver_service_->template object_resolver<Type>();
|
||||||
|
if (!resolver) {
|
||||||
|
return utils::failure(utils::error(error_code::UnknownType, "Missing object resolver for inserted type."));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object_cache_.is_loaded<Type>(utils::identifier{pk})) {
|
||||||
|
return utils::ok(object::object_ptr(object_cache_.acquire_proxy<Type>(utils::identifier{pk}, resolver)));
|
||||||
|
}
|
||||||
|
|
||||||
select_query_builder eqb(schema_);
|
select_query_builder eqb(schema_);
|
||||||
auto data = eqb.build<Type>(*it->second.table().primary_key_column() == pk);
|
auto data = eqb.build<Type>(*it->second.table().primary_key_column() == pk);
|
||||||
if (!data.is_ok()) {
|
if (!data.is_ok()) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public:
|
|||||||
table(const char *name); // NOLINT(*-explicit-constructor)
|
table(const char *name); // NOLINT(*-explicit-constructor)
|
||||||
table(const std::string& name); // NOLINT(*-explicit-constructor)
|
table(const std::string& name); // NOLINT(*-explicit-constructor)
|
||||||
table(const std::string& name, const std::vector<table_column>& columns);
|
table(const std::string& name, const std::vector<table_column>& columns);
|
||||||
|
table(const std::string& name, const std::vector<table_column>& columns, const std::string& join_column, const std::string& inverse_join_column);
|
||||||
table(const table& other);
|
table(const table& other);
|
||||||
table& operator=(const table& other);
|
table& operator=(const table& other);
|
||||||
table(table&& other) noexcept;
|
table(table&& other) noexcept;
|
||||||
@@ -44,11 +45,11 @@ public:
|
|||||||
[[nodiscard]] bool has_primary_key() const;
|
[[nodiscard]] bool has_primary_key() const;
|
||||||
|
|
||||||
[[nodiscard]] const table_column* primary_key_column() const;
|
[[nodiscard]] const table_column* primary_key_column() const;
|
||||||
[[nodiscard]] const std::string& join_column_name() const;
|
[[nodiscard]] const table_column* join_column() const;
|
||||||
[[nodiscard]] const std::string& inverse_join_column_name() const;
|
[[nodiscard]] const table_column* inverse_join_column() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
table(std::string name, std::string alias, const std::vector<table_column>& columns);
|
table(std::string name, std::string alias, const std::vector<table_column>& columns, const std::string& join_column, const std::string& inverse_join_column);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend table_column;
|
friend table_column;
|
||||||
@@ -60,8 +61,8 @@ private:
|
|||||||
std::vector<table_column> columns_;
|
std::vector<table_column> columns_;
|
||||||
|
|
||||||
int pk_column_index_{-1};
|
int pk_column_index_{-1};
|
||||||
std::string join_column_name_;
|
int join_column_index_{-1};
|
||||||
std::string inverse_join_column_name_;
|
int inverse_join_column_index_{-1};
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Type = table>
|
template<typename Type = table>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "matador/object/attribute.hpp"
|
#include "matador/object/attribute.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/dialect.hpp"
|
||||||
#include "matador/sql/connection_info.hpp"
|
#include "matador/sql/connection_info.hpp"
|
||||||
#include "matador/sql/execute_result.hpp"
|
#include "matador/sql/execute_result.hpp"
|
||||||
|
|
||||||
@@ -17,13 +18,11 @@ using blob_type_t = std::vector<unsigned char>;
|
|||||||
}
|
}
|
||||||
|
|
||||||
namespace matador::sql {
|
namespace matador::sql {
|
||||||
|
|
||||||
struct query_context;
|
struct query_context;
|
||||||
class query_result_impl;
|
class query_result_impl;
|
||||||
class statement_impl;
|
class statement_impl;
|
||||||
|
|
||||||
class connection_impl
|
class connection_impl {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
virtual ~connection_impl() = default;
|
virtual ~connection_impl() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
#include "matador/sql/abstract_sql_logger.hpp"
|
#include "matador/sql/abstract_sql_logger.hpp"
|
||||||
#include "matador/sql/error_code.hpp"
|
#include "matador/sql/error_code.hpp"
|
||||||
|
#include "matador/sql/execute_result.hpp"
|
||||||
#include "matador/sql/query_result.hpp"
|
#include "matador/sql/query_result.hpp"
|
||||||
#include "matador/sql/interface/statement_proxy.hpp"
|
#include "matador/sql/interface/statement_proxy.hpp"
|
||||||
|
|
||||||
#include "matador/object/basic_repository.hpp"
|
|
||||||
|
|
||||||
#include "matador/utils/error.hpp"
|
#include "matador/utils/error.hpp"
|
||||||
#include "matador/utils/result.hpp"
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
@@ -172,7 +171,8 @@ template<class Type>
|
|||||||
utils::result<query_result<Type>, utils::error> statement::fetch() {
|
utils::result<query_result<Type>, utils::error> statement::fetch() {
|
||||||
std::cout << statement_proxy_->sql() << std::endl;
|
std::cout << statement_proxy_->sql() << std::endl;
|
||||||
statement_proxy_->statement_->query_.result_type = typeid(Type);
|
statement_proxy_->statement_->query_.result_type = typeid(Type);
|
||||||
return statement_proxy_->fetch(*bindings_).and_then([this](std::unique_ptr<query_result_impl> &&value) {
|
return statement_proxy_->fetch(*bindings_)
|
||||||
|
.and_then([this](std::unique_ptr<query_result_impl> &&value) -> utils::result<query_result<Type>, utils::error> {
|
||||||
auto resolver = statement_proxy_->statement_->query_.resolver->object_resolver<Type>();
|
auto resolver = statement_proxy_->statement_->query_.resolver->object_resolver<Type>();
|
||||||
const auto prototype = value->prototype();
|
const auto prototype = value->prototype();
|
||||||
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), resolver, [prototype] {
|
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), resolver, [prototype] {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define QUERY_RESULT_HPP
|
#define QUERY_RESULT_HPP
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <optional>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
@@ -21,6 +20,7 @@ template < typename ValueType >
|
|||||||
class ok {
|
class ok {
|
||||||
public:
|
public:
|
||||||
using value_type = ValueType;
|
using value_type = ValueType;
|
||||||
|
constexpr ok() = default;
|
||||||
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)) {}
|
||||||
|
|
||||||
@@ -49,21 +49,49 @@ public:
|
|||||||
|
|
||||||
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_; }
|
ErrorType& value() { return error_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ErrorType error_;
|
ErrorType error_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
template <typename ValueType, typename Func, bool IsVoidValue = std::is_void_v<ValueType>>
|
||||||
|
struct map_result_value_type;
|
||||||
|
|
||||||
|
template <typename ValueType, typename Func>
|
||||||
|
struct map_result_value_type<ValueType, Func, false> {
|
||||||
|
using type = std::invoke_result_t<Func, ValueType&&>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueType, typename Func>
|
||||||
|
struct map_result_value_type<ValueType, Func, true> {
|
||||||
|
using type = std::invoke_result_t<Func>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueType, typename Func, bool IsVoidValue = std::is_void_v<ValueType>>
|
||||||
|
struct and_then_result_type;
|
||||||
|
|
||||||
|
template <typename ValueType, typename Func>
|
||||||
|
struct and_then_result_type<ValueType, Func, false> {
|
||||||
|
using type = std::invoke_result_t<Func, ValueType&&>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename ValueType, typename Func>
|
||||||
|
struct and_then_result_type<ValueType, Func, true> {
|
||||||
|
using type = std::invoke_result_t<Func>;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
template < typename ValueType, typename ErrorType >
|
template < typename ValueType, typename ErrorType >
|
||||||
class result {
|
class result {
|
||||||
public:
|
public:
|
||||||
using value_type = ValueType;
|
using value_type = ValueType;
|
||||||
using error_type = ErrorType;
|
using error_type = ErrorType;
|
||||||
|
|
||||||
result() : result_(ValueType{}) {}
|
result() : result_(ok<value_type>{}) {}
|
||||||
result(ok<value_type> value) : result_(std::move(value.release())) {} // NOLINT(*-explicit-constructor)
|
result(ok<value_type> value) : result_(std::move(value)) {} // NOLINT(*-explicit-constructor)
|
||||||
result(failure<error_type> error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor)
|
result(failure<error_type> error) : result_(std::move(error)) {} // NOLINT(*-explicit-constructor)
|
||||||
result(const result &x) = default;
|
result(const result &x) = default;
|
||||||
result& operator=(const result &x) = default;
|
result& operator=(const result &x) = default;
|
||||||
result(result &&x) = default;
|
result(result &&x) = default;
|
||||||
@@ -71,123 +99,128 @@ public:
|
|||||||
|
|
||||||
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
|
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
|
||||||
|
|
||||||
[[nodiscard]] bool is_ok() const { return std::holds_alternative<value_type>(result_); }
|
[[nodiscard]] bool is_ok() const {
|
||||||
[[nodiscard]] bool is_error() const { return std::holds_alternative<error_type>(result_); }
|
return std::holds_alternative<ok<value_type>>(result_);
|
||||||
|
}
|
||||||
ValueType&& release() { return std::move(std::get<value_type>(result_)); }
|
[[nodiscard]] bool is_error() const {
|
||||||
ErrorType&& release_error() { return std::move(std::get<error_type>(result_)); }
|
return std::holds_alternative<failure<error_type>>(result_);
|
||||||
|
|
||||||
const ValueType& value() const { return std::get<value_type>(result_); }
|
|
||||||
ValueType& value() { return std::get<value_type>(result_); }
|
|
||||||
const ErrorType& err() const { return std::get<error_type>(result_); }
|
|
||||||
ErrorType err() { return std::get<error_type>(result_); }
|
|
||||||
|
|
||||||
constexpr const ValueType* operator->() const { return &value(); }
|
|
||||||
constexpr ValueType* operator->() { return &std::get<value_type>(result_); }
|
|
||||||
|
|
||||||
constexpr const ValueType& operator*() const& noexcept { return value(); }
|
|
||||||
constexpr ValueType& operator*() & noexcept { return value(); }
|
|
||||||
|
|
||||||
template<typename Func,
|
|
||||||
typename SecondValueType = std::invoke_result_t<Func, ValueType >>
|
|
||||||
result<SecondValueType, ErrorType> map(Func &&f) {
|
|
||||||
if (is_ok()) {
|
|
||||||
return result<SecondValueType, ErrorType>(ok(f(release())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result<SecondValueType, ErrorType>(failure(release_error()));
|
template <typename T = ValueType>
|
||||||
|
std::enable_if_t<!std::is_void_v<T>, T&&> release() {
|
||||||
|
return std::move(std::get<ok<value_type>>(result_).release());
|
||||||
|
}
|
||||||
|
ErrorType&& release_error() {
|
||||||
|
return std::move(std::get<failure<error_type>>(result_).release());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = ValueType>
|
||||||
|
std::enable_if_t<!std::is_void_v<T>, const T&> value() const {
|
||||||
|
return std::get<ok<value_type>>(result_).value();
|
||||||
|
}
|
||||||
|
template <typename T = ValueType>
|
||||||
|
std::enable_if_t<!std::is_void_v<T>, T&> value() {
|
||||||
|
return std::get<ok<value_type>>(result_).value();
|
||||||
|
}
|
||||||
|
const ErrorType& err() const {
|
||||||
|
return std::get<failure<error_type>>(result_).value();
|
||||||
|
}
|
||||||
|
ErrorType& err() {
|
||||||
|
return std::get<failure<error_type>>(result_).value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = ValueType>
|
||||||
|
constexpr std::enable_if_t<!std::is_void_v<T>, const T*> operator->() const {
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
template <typename T = ValueType>
|
||||||
|
constexpr std::enable_if_t<!std::is_void_v<T>, T*> operator->() {
|
||||||
|
return &value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = ValueType>
|
||||||
|
constexpr std::enable_if_t<!std::is_void_v<T>, const T&> operator*() const& noexcept {
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
template <typename T = ValueType>
|
||||||
|
constexpr std::enable_if_t<!std::is_void_v<T>, T&> operator*() & noexcept {
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Func,
|
||||||
|
typename SecondValueType = typename detail::map_result_value_type<ValueType, Func>::type>
|
||||||
|
result<SecondValueType, ErrorType> map(Func &&f) {
|
||||||
|
if (is_error()) {
|
||||||
|
return failure<ErrorType>(release_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (std::is_void_v<ValueType>) {
|
||||||
|
if constexpr (std::is_void_v<SecondValueType>) {
|
||||||
|
std::invoke(std::forward<Func>(f));
|
||||||
|
return ok<void>{};
|
||||||
|
} else {
|
||||||
|
return ok<SecondValueType>(std::invoke(std::forward<Func>(f)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if constexpr (std::is_void_v<SecondValueType>) {
|
||||||
|
std::invoke(std::forward<Func>(f), release());
|
||||||
|
return ok<void>{};
|
||||||
|
} else {
|
||||||
|
return ok<SecondValueType>(std::invoke(std::forward<Func>(f), release()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Func,
|
template<typename Func,
|
||||||
typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type>
|
typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type>
|
||||||
result<SecondErrorType, ErrorType> map_error(Func &&f) {
|
result<SecondErrorType, ErrorType> map_error(Func &&f) {
|
||||||
if (!is_ok()) {
|
if (!is_error()) {
|
||||||
return result<SecondErrorType, ErrorType>(ok(release()));
|
return failure<SecondErrorType>{std::invoke(std::forward<Func>(f), release_error())};
|
||||||
}
|
}
|
||||||
|
|
||||||
return result<SecondErrorType, ErrorType>(error(release_error()));
|
if constexpr (std::is_void_v<ValueType>) {
|
||||||
|
return ok<void>{};
|
||||||
|
} else {
|
||||||
|
return ok<ValueType>(release());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func,
|
template <typename Func,
|
||||||
typename SecondValueType = typename std::invoke_result_t<Func, ValueType>::value_type>
|
typename ReturnResult = typename detail::and_then_result_type<ValueType, Func>::type>
|
||||||
result<SecondValueType, ErrorType> and_then(Func &&f) {
|
ReturnResult and_then(Func &&f) {
|
||||||
|
static_assert(is_result<ReturnResult>::value, "and_then() callback must return matador::utils::result");
|
||||||
|
|
||||||
if (is_ok()) {
|
if (is_ok()) {
|
||||||
return f(release());
|
if constexpr (std::is_void_v<ValueType>) {
|
||||||
|
return std::invoke(std::forward<Func>(f));
|
||||||
|
} else {
|
||||||
|
return std::invoke(std::forward<Func>(f), release());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result<SecondValueType, ErrorType>(failure(release_error()));
|
return ReturnResult(failure<ErrorType>(release_error()));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Func,
|
template <typename Func,
|
||||||
typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type>
|
typename FailureType = std::invoke_result_t<Func, ErrorType&&>,
|
||||||
|
typename SecondErrorType = typename FailureType::value_type>
|
||||||
result<ValueType, SecondErrorType> or_else(Func &&f) {
|
result<ValueType, SecondErrorType> or_else(Func &&f) {
|
||||||
if (is_error()) {
|
if (is_error()) {
|
||||||
return f(err());
|
return result<ValueType, SecondErrorType>(
|
||||||
|
std::invoke(std::forward<Func>(f), release_error())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result<ValueType, SecondErrorType>(ok(release()));
|
if constexpr (std::is_void_v<ValueType>) {
|
||||||
|
return ok<void>{};
|
||||||
|
} else {
|
||||||
|
return ok<ValueType>(release());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::variant<value_type, error_type> result_;
|
std::variant<ok<value_type>, failure<error_type>> result_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template < typename ErrorType >
|
|
||||||
class result<void, ErrorType>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = void;
|
|
||||||
using error_type = ErrorType;
|
|
||||||
|
|
||||||
result() = default;
|
|
||||||
result(ok<void> /*value*/) {}
|
|
||||||
result(failure<error_type> error) : result_(std::move(error.release())) {} // NOLINT(*-explicit-constructor)
|
|
||||||
result(const result &x) = default;
|
|
||||||
result& operator=(const result &x) = default;
|
|
||||||
result(result &&x) = default;
|
|
||||||
result& operator=(result &&x) = default;
|
|
||||||
|
|
||||||
operator bool() const { return is_ok(); } // NOLINT(*-explicit-constructor)
|
|
||||||
|
|
||||||
[[nodiscard]] bool is_ok() const { return !result_.has_value(); }
|
|
||||||
[[nodiscard]] bool is_error() const { return result_.has_value(); }
|
|
||||||
|
|
||||||
ErrorType&& release_error() { return std::move(*result_); }
|
|
||||||
|
|
||||||
const ErrorType& err() const { return result_.value(); }
|
|
||||||
ErrorType err() { return result_.value(); }
|
|
||||||
|
|
||||||
template<typename Func, typename SecondValueType = std::invoke_result_t<Func>>
|
|
||||||
result<SecondValueType, ErrorType> map(Func &&f) {
|
|
||||||
if (is_ok()) {
|
|
||||||
return result<SecondValueType, ErrorType>(ok(f()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result<SecondValueType, ErrorType>(failure(release_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Func>
|
|
||||||
result and_then(Func &&f) {
|
|
||||||
if (is_ok()) {
|
|
||||||
return f();
|
|
||||||
}
|
|
||||||
|
|
||||||
return result(failure(release_error()));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Func, typename SecondErrorType = typename std::invoke_result_t<Func, ErrorType >::value_type>
|
|
||||||
result<void, SecondErrorType> or_else(Func &&f) {
|
|
||||||
if (is_error()) {
|
|
||||||
return f(err());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result<void, SecondErrorType>(ok<void>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::optional<error_type> result_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //QUERY_RESULT_HPP
|
#endif //QUERY_RESULT_HPP
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ public:
|
|||||||
* @param args Passed arguments
|
* @param args Passed arguments
|
||||||
*/
|
*/
|
||||||
template <typename F, typename... Args>
|
template <typename F, typename... Args>
|
||||||
auto schedule(F&& func, Args&&... args) -> result_fut<std::result_of_t<F(cancel_token&, Args...)>> {
|
auto schedule(F&& func, Args&&... args) -> result_fut<std::invoke_result_t<F, cancel_token&, Args...>> {
|
||||||
using return_type = std::result_of_t<F(cancel_token&, Args...)>;
|
using return_type = std::invoke_result_t<F, cancel_token&, Args...>;
|
||||||
const auto token = std::make_shared<cancel_token>();
|
const auto token = std::make_shared<cancel_token>();
|
||||||
|
|
||||||
auto task_ptr = std::make_shared<std::packaged_task<return_type()>>(
|
auto task_ptr = std::make_shared<std::packaged_task<return_type()>>(
|
||||||
@@ -73,7 +73,7 @@ public:
|
|||||||
if (!running_) {
|
if (!running_) {
|
||||||
return failure(std::string("Thread pool is shut down, cannot schedule new tasks."));
|
return failure(std::string("Thread pool is shut down, cannot schedule new tasks."));
|
||||||
}
|
}
|
||||||
tasks_.emplace_back([task_ptr, token] {
|
tasks_.emplace_back([task_ptr] {
|
||||||
try { (*task_ptr)(); }
|
try { (*task_ptr)(); }
|
||||||
catch (...) { /* Prevent exception escape */ }
|
catch (...) { /* Prevent exception escape */ }
|
||||||
}, token);
|
}, token);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
if (!res.is_ok()) {
|
if (!res.is_ok()) {
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
return *res;
|
return res.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
|
|
||||||
#include "matador/utils/result.hpp"
|
#include "matador/utils/result.hpp"
|
||||||
#include "matador/utils/error.hpp"
|
#include "matador/utils/error.hpp"
|
||||||
|
#include "matador/utils/export.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
namespace matador::utils {
|
namespace matador::utils {
|
||||||
|
|
||||||
class version {
|
class MATADOR_UTILS_API version final {
|
||||||
public:
|
public:
|
||||||
version() = default;
|
version() = default;
|
||||||
~version() = default;
|
~version() = default;
|
||||||
|
|||||||
@@ -32,6 +32,10 @@ std::string attribute::full_name() const {
|
|||||||
return owner ? owner->name() + "." + name_ : name_;
|
return owner ? owner->name() + "." + name_ : name_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t attribute::index() const {
|
||||||
|
return index_;
|
||||||
|
}
|
||||||
|
|
||||||
const utils::field_attributes &attribute::attributes() const {
|
const utils::field_attributes &attribute::attributes() const {
|
||||||
return options_;
|
return options_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,11 +21,11 @@ std::shared_ptr<class object> basic_object_info::object() const {
|
|||||||
return object_;
|
return object_;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<attribute>& basic_object_info::attributes() const {
|
const std::vector<attribute>& basic_object_info::attributes() const {
|
||||||
return object_->attributes();
|
return object_->attributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<class restriction>& basic_object_info::constraints() const {
|
const std::list<restriction>& basic_object_info::constraints() const {
|
||||||
return object_->constraints();
|
return object_->constraints();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ const utils::identifier& basic_object_info::primary_key() const {
|
|||||||
return object_->primary_key();
|
return object_->primary_key();
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute* basic_object_info::primary_key_attribute() const {
|
const attribute* basic_object_info::primary_key_attribute() const {
|
||||||
return object_->primary_key_attribute();
|
return object_->primary_key_attribute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ utils::result<basic_object_info_ref, utils::error> basic_repository::basic_info(
|
|||||||
return utils::ok(basic_object_info_ref{it->info()});
|
return utils::ok(basic_object_info_ref{it->info()});
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<attribute*, utils::error> basic_repository::primary_key_attribute(const std::type_index &ti) const {
|
utils::result<const attribute*, utils::error> basic_repository::primary_key_attribute(const std::type_index &ti) const {
|
||||||
const auto it = find_node(ti);
|
const auto it = find_node(ti);
|
||||||
if (it == end()) {
|
if (it == end()) {
|
||||||
return utils::failure(make_error(error_code::NodeNotFound, "Node '" + std::string(ti.name()) + "' not found."));
|
return utils::failure(make_error(error_code::NodeNotFound, "Node '" + std::string(ti.name()) + "' not found."));
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ const attribute& object::create_attribute(std::string name, const std::shared_pt
|
|||||||
return obj->attributes_.emplace_back(std::move(attr));
|
return obj->attributes_.emplace_back(std::move(attr));
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute* object::primary_key_attribute() const {
|
const attribute* object::primary_key_attribute() const {
|
||||||
return pk_attribute_;
|
return pk_column_index_ != -1 ? &attributes_.at(pk_column_index_) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const utils::identifier& object::primary_key() const {
|
const utils::identifier& object::primary_key() const {
|
||||||
@@ -19,7 +19,19 @@ const utils::identifier& object::primary_key() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool object::has_primary_key() const {
|
bool object::has_primary_key() const {
|
||||||
return pk_attribute_ != nullptr;
|
return pk_column_index_ != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool object::is_relation_object() const {
|
||||||
|
return join_column_index_ != -1 && inverse_join_column_index_ != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attribute * object::join_attribute() const {
|
||||||
|
return join_column_index_ != -1 ? &attributes_.at(join_column_index_) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attribute * object::inverse_join_attribute() const {
|
||||||
|
return inverse_join_column_index_ != -1 ? &attributes_.at(inverse_join_column_index_) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& object::name() const {
|
const std::string& object::name() const {
|
||||||
@@ -38,7 +50,7 @@ size_t object::attribute_count() const {
|
|||||||
return attributes_.size();
|
return attributes_.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::list<attribute>& object::attributes() const {
|
const std::vector<attribute>& object::attributes() const {
|
||||||
return attributes_;
|
return attributes_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
object_generator::object_generator(basic_repository &repo, const std::shared_ptr<object> &object)
|
object_generator::object_generator(basic_repository &repo, const std::shared_ptr<object> &object)
|
||||||
: repo_(repo)
|
: repo_(repo)
|
||||||
, object_(object) {}
|
, object_(object) {
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<object> object_generator::acquire_object(basic_repository &repo, const std::type_index &ti, const std::string &name) {
|
std::shared_ptr<object> object_generator::acquire_object(basic_repository &repo, const std::type_index &ti, const std::string &name) {
|
||||||
if (repo.has_object_for_type(ti)) {
|
if (repo.has_object_for_type(ti)) {
|
||||||
@@ -29,7 +30,7 @@ void object_generator::create_pk_constraint(const std::string& name) const {
|
|||||||
if (pk_attr == std::end(object_->attributes_)) {
|
if (pk_attr == std::end(object_->attributes_)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
restriction pk_constraint(*pk_attr);
|
restriction pk_constraint(pk_attr->index());
|
||||||
pk_constraint.options_ |= utils::constraints::PrimaryKey;
|
pk_constraint.options_ |= utils::constraints::PrimaryKey;
|
||||||
pk_constraint.owner_ = object_;
|
pk_constraint.owner_ = object_;
|
||||||
object_->constraints_.emplace_back(std::move(pk_constraint));
|
object_->constraints_.emplace_back(std::move(pk_constraint));
|
||||||
@@ -40,19 +41,33 @@ void object_generator::create_unique_constraint(const std::string& name) const {
|
|||||||
if (pk_attr == std::end(object_->attributes_)) {
|
if (pk_attr == std::end(object_->attributes_)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
restriction pk_constraint(*pk_attr);
|
restriction pk_constraint(pk_attr->index());
|
||||||
pk_constraint.options_ |= utils::constraints::Unique;
|
pk_constraint.options_ |= utils::constraints::Unique;
|
||||||
pk_constraint.owner_ = object_;
|
pk_constraint.owner_ = object_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::list<attribute>::iterator object_generator::find_attribute_by_name(const std::string& name) const {
|
std::vector<attribute>::iterator object_generator::find_attribute_by_name(const std::string &name) const {
|
||||||
return std::find_if(std::begin(object_->attributes_), std::end(object_->attributes_), [&name](const attribute &elem) {
|
return std::find_if(std::begin(object_->attributes_), std::end(object_->attributes_), [&name](const attribute &elem) {
|
||||||
return elem.name() == name;
|
return elem.name() == name;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void object_generator::prepare_primary_key(attribute& ref, utils::identifier &&pk) const {
|
void object_generator::prepare_primary_key(const attribute &ref, utils::identifier &&pk) const {
|
||||||
object_->pk_attribute_ = &ref;
|
object_->pk_column_index_ = static_cast<int>(ref.index_);
|
||||||
object_->pk_identifier_ = std::move(pk);
|
object_->pk_identifier_ = std::move(pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void object_generator::prepare_relation_table(const std::string &join_column, const std::string &inverse_join_column) const {
|
||||||
|
auto it = find_attribute_by_name(join_column);
|
||||||
|
if (it == std::end(object_->attributes_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
object_->join_column_index_ = static_cast<int>(it->index_);
|
||||||
|
|
||||||
|
it = find_attribute_by_name(inverse_join_column);
|
||||||
|
if (it == std::end(object_->attributes_)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
object_->inverse_join_column_index_ = static_cast<int>(it->index_);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,23 @@
|
|||||||
#include "matador/object/object.hpp"
|
#include "matador/object/object.hpp"
|
||||||
|
|
||||||
namespace matador::object {
|
namespace matador::object {
|
||||||
restriction::restriction(const class attribute& attr)
|
restriction::restriction(const size_t attr_index)
|
||||||
: attr_(attr) {}
|
: index_(attr_index) {}
|
||||||
|
|
||||||
const class attribute& restriction::attribute() const {
|
size_t restriction::attribute_index() const {
|
||||||
return attr_;
|
return index_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string restriction::column_name() const {
|
std::string restriction::column_name() const {
|
||||||
return attr_.name();
|
const auto o = owner_.lock();
|
||||||
|
return o ? o->attributes().at(index_).name() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
utils::constraints restriction::options() const {
|
||||||
|
const auto o = owner_.lock();
|
||||||
|
return o ? o->attributes().at(index_).attributes().options() : utils::constraints::None;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<object> restriction::owner() const {
|
std::shared_ptr<object> restriction::owner() const {
|
||||||
return owner_.lock();
|
return owner_.lock();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,98 +3,122 @@
|
|||||||
#include <matador/utils/errors.hpp>
|
#include <matador/utils/errors.hpp>
|
||||||
|
|
||||||
namespace matador::utils {
|
namespace matador::utils {
|
||||||
|
namespace {
|
||||||
|
|
||||||
version::version(unsigned int major, unsigned int minor, unsigned int patch)
|
bool parse_uint_component(const std::string &text, std::size_t &pos, unsigned int &value) {
|
||||||
|
if (pos >= text.size() || text[pos] < '0' || text[pos] > '9') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int result{};
|
||||||
|
|
||||||
|
while (pos < text.size() && text[pos] >= '0' && text[pos] <= '9') {
|
||||||
|
const auto digit = static_cast<unsigned int>(text[pos] - '0');
|
||||||
|
|
||||||
|
if (result > (std::numeric_limits<unsigned int>::max() - digit) / 10) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result * 10 + digit;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse_dot(const std::string &text, std::size_t &pos) {
|
||||||
|
if (pos >= text.size() || text[pos] != '.') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
++pos;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
version::version(const unsigned int major, const unsigned int minor, const unsigned int patch)
|
||||||
: major_(major)
|
: major_(major)
|
||||||
, minor_(minor)
|
, minor_(minor)
|
||||||
, patch_(patch)
|
, patch_(patch) {
|
||||||
{}
|
}
|
||||||
|
|
||||||
bool version::operator==(const version &x) const
|
bool version::operator==(const version& x) const {
|
||||||
{
|
|
||||||
return major_ == x.major_ &&
|
return major_ == x.major_ &&
|
||||||
minor_ == x.minor_ &&
|
minor_ == x.minor_ &&
|
||||||
patch_ == x.patch_;
|
patch_ == x.patch_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool version::operator!=(const version &x) const
|
bool version::operator!=(const version& x) const {
|
||||||
{
|
|
||||||
return !(*this == x);
|
return !(*this == x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool version::operator>(const version &x) const
|
bool version::operator>(const version& x) const {
|
||||||
{
|
|
||||||
return !(*this <= x);
|
return !(*this <= x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool version::operator>=(const version &x) const
|
bool version::operator>=(const version& x) const {
|
||||||
{
|
|
||||||
return !(*this < x);
|
return !(*this < x);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool version::operator<(const version &x) const
|
bool version::operator<(const version& x) const {
|
||||||
{
|
|
||||||
return (major_ < x.major_) ||
|
return (major_ < x.major_) ||
|
||||||
(major_ == x.major_ && minor_ < x.minor_) ||
|
(major_ == x.major_ && minor_ < x.minor_) ||
|
||||||
(major_ == x.major_ && minor_ == x.minor_ && patch_ < x.patch_);
|
(major_ == x.major_ && minor_ == x.minor_ && patch_ < x.patch_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool version::operator<=(const version &x) const
|
bool version::operator<=(const version& x) const {
|
||||||
{
|
|
||||||
return *this < x || *this == x;
|
return *this < x || *this == x;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string version::str() const
|
std::string version::str() const {
|
||||||
{
|
return std::to_string(major_) + "." +
|
||||||
char buf[32];
|
std::to_string(minor_) + "." +
|
||||||
sprintf(buf, "%d.%d.%d", major_, minor_, patch_);
|
std::to_string(patch_);
|
||||||
return buf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream &operator<<(std::ostream &out, const version &v)
|
std::ostream& operator<<(std::ostream& out, const version& v) {
|
||||||
{
|
|
||||||
out << v.str();
|
out << v.str();
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
result<version, error> version::from_string(const std::string &version_string)
|
result<version, error> version::from_string(const std::string& version_string) {
|
||||||
{
|
unsigned int major{};
|
||||||
version result;
|
unsigned int minor{};
|
||||||
if (const auto ret = sscanf(version_string.c_str(), "%u.%u.%u", &result.major_, &result.minor_, &result.patch_); ret != 3) {
|
unsigned int patch{};
|
||||||
|
|
||||||
|
if (std::size_t pos{}; !parse_uint_component(version_string, pos, major) ||
|
||||||
|
!parse_dot(version_string, pos) ||
|
||||||
|
!parse_uint_component(version_string, pos, minor) ||
|
||||||
|
!parse_dot(version_string, pos) ||
|
||||||
|
!parse_uint_component(version_string, pos, patch) ||
|
||||||
|
pos != version_string.size()) {
|
||||||
return failure(error(utils_error::InvalidVersionString, version_string));
|
return failure(error(utils_error::InvalidVersionString, version_string));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ok(result);
|
return ok(version{major, minor, patch});}
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int version::major() const
|
unsigned int version::major() const {
|
||||||
{
|
|
||||||
return major_;
|
return major_;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int version::minor() const
|
unsigned int version::minor() const {
|
||||||
{
|
|
||||||
return minor_;
|
return minor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int version::patch() const
|
unsigned int version::patch() const {
|
||||||
{
|
|
||||||
return patch_;
|
return patch_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void version::major(unsigned int m)
|
void version::major(unsigned int m) {
|
||||||
{
|
|
||||||
major_ = m;
|
major_ = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void version::minor(unsigned int m)
|
void version::minor(unsigned int m) {
|
||||||
{
|
|
||||||
minor_ = m;
|
minor_ = m;
|
||||||
}
|
}
|
||||||
|
|
||||||
void version::patch(unsigned int p)
|
void version::patch(unsigned int p) {
|
||||||
{
|
|
||||||
patch_ = p;
|
patch_ = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,10 @@ add_library(matador-orm STATIC
|
|||||||
../../include/matador/query/criteria/logical_criteria.hpp
|
../../include/matador/query/criteria/logical_criteria.hpp
|
||||||
../../include/matador/query/criteria_evaluator.hpp
|
../../include/matador/query/criteria_evaluator.hpp
|
||||||
../../include/matador/query/database.hpp
|
../../include/matador/query/database.hpp
|
||||||
|
../../include/matador/query/delete_query_builder.hpp
|
||||||
|
../../include/matador/query/delete_step.hpp
|
||||||
../../include/matador/query/error_code.hpp
|
../../include/matador/query/error_code.hpp
|
||||||
|
../../include/matador/query/execute_step.hpp
|
||||||
../../include/matador/query/expression/abstract_column_expression.hpp
|
../../include/matador/query/expression/abstract_column_expression.hpp
|
||||||
../../include/matador/query/expression/binary_column_expression.hpp
|
../../include/matador/query/expression/binary_column_expression.hpp
|
||||||
../../include/matador/query/expression/column_expression.hpp
|
../../include/matador/query/expression/column_expression.hpp
|
||||||
@@ -68,9 +71,9 @@ add_library(matador-orm STATIC
|
|||||||
../../include/matador/query/key_value_generator.hpp
|
../../include/matador/query/key_value_generator.hpp
|
||||||
../../include/matador/query/manual_pk_generator.hpp
|
../../include/matador/query/manual_pk_generator.hpp
|
||||||
../../include/matador/query/meta_table_macro.hpp
|
../../include/matador/query/meta_table_macro.hpp
|
||||||
../../include/matador/query/meta_table_macro.hpp
|
|
||||||
../../include/matador/query/query.hpp
|
../../include/matador/query/query.hpp
|
||||||
../../include/matador/query/query_builder.hpp
|
../../include/matador/query/query_builder.hpp
|
||||||
|
../../include/matador/query/query_builder_utils.hpp
|
||||||
../../include/matador/query/query_collection_resolver.hpp
|
../../include/matador/query/query_collection_resolver.hpp
|
||||||
../../include/matador/query/query_column.hpp
|
../../include/matador/query/query_column.hpp
|
||||||
../../include/matador/query/query_contexts.hpp
|
../../include/matador/query/query_contexts.hpp
|
||||||
@@ -80,6 +83,7 @@ add_library(matador-orm STATIC
|
|||||||
../../include/matador/query/query_part.hpp
|
../../include/matador/query/query_part.hpp
|
||||||
../../include/matador/query/query_utils.hpp
|
../../include/matador/query/query_utils.hpp
|
||||||
../../include/matador/query/schema.hpp
|
../../include/matador/query/schema.hpp
|
||||||
|
../../include/matador/query/schema_utils.hpp
|
||||||
../../include/matador/query/select_query_builder.hpp
|
../../include/matador/query/select_query_builder.hpp
|
||||||
../../include/matador/query/sequence_pk_generator.hpp
|
../../include/matador/query/sequence_pk_generator.hpp
|
||||||
../../include/matador/query/session.hpp
|
../../include/matador/query/session.hpp
|
||||||
@@ -104,9 +108,9 @@ add_library(matador-orm STATIC
|
|||||||
../../include/matador/sql/interface/query_result_reader.hpp
|
../../include/matador/sql/interface/query_result_reader.hpp
|
||||||
../../include/matador/sql/interface/statement_impl.hpp
|
../../include/matador/sql/interface/statement_impl.hpp
|
||||||
../../include/matador/sql/interface/statement_proxy.hpp
|
../../include/matador/sql/interface/statement_proxy.hpp
|
||||||
../../include/matador/sql/internal/joined_collection_resolver_producer.hpp
|
|
||||||
../../include/matador/sql/internal/identifier_reader.hpp
|
../../include/matador/sql/internal/identifier_reader.hpp
|
||||||
../../include/matador/sql/internal/identifier_statement_binder.hpp
|
../../include/matador/sql/internal/identifier_statement_binder.hpp
|
||||||
|
../../include/matador/sql/internal/joined_collection_resolver_producer.hpp
|
||||||
../../include/matador/sql/internal/object_resolver_producer.hpp
|
../../include/matador/sql/internal/object_resolver_producer.hpp
|
||||||
../../include/matador/sql/internal/object_result_binder.hpp
|
../../include/matador/sql/internal/object_result_binder.hpp
|
||||||
../../include/matador/sql/internal/pk_reader.hpp
|
../../include/matador/sql/internal/pk_reader.hpp
|
||||||
@@ -188,6 +192,7 @@ add_library(matador-orm STATIC
|
|||||||
query/query_part.cpp
|
query/query_part.cpp
|
||||||
query/query_utils.cpp
|
query/query_utils.cpp
|
||||||
query/schema.cpp
|
query/schema.cpp
|
||||||
|
query/schema_utils.cpp
|
||||||
query/select_query_builder.cpp
|
query/select_query_builder.cpp
|
||||||
query/sequence_pk_generator.cpp
|
query/sequence_pk_generator.cpp
|
||||||
query/session.cpp
|
query/session.cpp
|
||||||
@@ -208,9 +213,9 @@ add_library(matador-orm STATIC
|
|||||||
sql/interface/query_result_reader.cpp
|
sql/interface/query_result_reader.cpp
|
||||||
sql/interface/statement_impl.cpp
|
sql/interface/statement_impl.cpp
|
||||||
sql/interface/statement_proxy.cpp
|
sql/interface/statement_proxy.cpp
|
||||||
sql/internal/joined_collection_resolver_producer.cpp
|
|
||||||
sql/internal/identifier_reader.cpp
|
sql/internal/identifier_reader.cpp
|
||||||
sql/internal/identifier_statement_binder.cpp
|
sql/internal/identifier_statement_binder.cpp
|
||||||
|
sql/internal/joined_collection_resolver_producer.cpp
|
||||||
sql/internal/object_resolver_producer.cpp
|
sql/internal/object_resolver_producer.cpp
|
||||||
sql/internal/object_result_binder.cpp
|
sql/internal/object_result_binder.cpp
|
||||||
sql/internal/query_result_pk_resolver.cpp
|
sql/internal/query_result_pk_resolver.cpp
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "matador/query/query_utils.hpp"
|
#include "matador/query/query_utils.hpp"
|
||||||
|
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
|
||||||
#include <matador/utils/convert.hpp>
|
#include <matador/utils/convert.hpp>
|
||||||
|
|||||||
@@ -13,61 +13,61 @@ namespace matador::query {
|
|||||||
criteria_ptr operator==(const table_column &col, const utils::identifier &id) {
|
criteria_ptr operator==(const table_column &col, const utils::identifier &id) {
|
||||||
utils::identifier_to_value_converter conv;
|
utils::identifier_to_value_converter conv;
|
||||||
|
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::EQUALS, conv.convert(id));
|
return std::make_unique<binary_criteria>(col, binary_operator::Equals, conv.convert(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator!=(const table_column &col, const utils::identifier &id) {
|
criteria_ptr operator!=(const table_column &col, const utils::identifier &id) {
|
||||||
utils::identifier_to_value_converter conv;
|
utils::identifier_to_value_converter conv;
|
||||||
|
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::NOT_EQUALS, conv.convert(id));
|
return std::make_unique<binary_criteria>(col, binary_operator::NotEquals, conv.convert(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator==(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator==(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::EQUALS, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::Equals, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator!=(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator!=(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::NOT_EQUALS, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::NotEquals, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator>(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator>(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThan, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator>=(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator>=(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::GREATER_THAN_OR_EQUAL, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::GreaterThanOrEqual, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator<(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator<(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThan, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator<=(const table_column &col, utils::placeholder p) {
|
criteria_ptr operator<=(const table_column &col, utils::placeholder p) {
|
||||||
return std::make_unique<binary_criteria>(col, binary_operator::LESS_THAN_OR_EQUAL, p);
|
return std::make_unique<binary_criteria>(col, binary_operator::LessThanOrEqual, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator==( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator==( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::EQUALS, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::Equals, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator!=( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator!=( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::NOT_EQUALS, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::NotEquals, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator>( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator>( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::GREATER_THAN, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::GreaterThan, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator>=( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator>=( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::GREATER_THAN_OR_EQUAL, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::GreaterThanOrEqual, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator<( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator<( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::LESS_THAN, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::LessThan, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator<=( const table_column& col_left, const table_column& col_right ) {
|
criteria_ptr operator<=( const table_column& col_left, const table_column& col_right ) {
|
||||||
return std::make_unique<binary_column_criteria>(col_left, binary_operator::LESS_THAN_OR_EQUAL, col_right);
|
return std::make_unique<binary_column_criteria>(col_left, binary_operator::LessThanOrEqual, col_right);
|
||||||
}
|
}
|
||||||
|
|
||||||
criteria_ptr operator&&(criteria_ptr left, criteria_ptr right) {
|
criteria_ptr operator&&(criteria_ptr left, criteria_ptr right) {
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
static const utils::enum_mapper<binary_operator> BinaryOperatorEnum({
|
static const utils::enum_mapper<binary_operator> BinaryOperatorEnum({
|
||||||
{binary_operator::EQUALS, "="},
|
{binary_operator::Equals, "="},
|
||||||
{binary_operator::NOT_EQUALS, "<>"},
|
{binary_operator::NotEquals, "<>"},
|
||||||
{binary_operator::GREATER_THAN, ">"},
|
{binary_operator::GreaterThan, ">"},
|
||||||
{binary_operator::GREATER_THAN_OR_EQUAL, ">="},
|
{binary_operator::GreaterThanOrEqual, ">="},
|
||||||
{binary_operator::LESS_THAN, "<"},
|
{binary_operator::LessThan, "<"},
|
||||||
{binary_operator::LESS_THAN_OR_EQUAL, "<="},
|
{binary_operator::LessThanOrEqual, "<="},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,8 @@ sql::query_context executable_query::compile(const sql::dialect& d) const {
|
|||||||
return compiler.build(*context_, d, std::nullopt);
|
return compiler.build(*context_, d, std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string executable_query::str(const sql::executor &exec) const {
|
std::string executable_query::str(const sql::dialect& d) const {
|
||||||
query_builder compiler;
|
return compile(d).sql;
|
||||||
return exec.str(compiler.build(*context_, exec.dialect(), std::nullopt));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ utils::result<sql::query_result<sql::record>, utils::error> fetchable_query::fet
|
|||||||
auto ctx = compiler.build(*context_, exec.dialect(), std::nullopt);
|
auto ctx = compiler.build(*context_, exec.dialect(), std::nullopt);
|
||||||
ctx.resolver = exec.resolver();
|
ctx.resolver = exec.resolver();
|
||||||
return exec.fetch(ctx)
|
return exec.fetch(ctx)
|
||||||
.and_then([](auto &&res) {
|
.and_then([](auto &&res) -> utils::result<sql::query_result<sql::record>, utils::error> {
|
||||||
const auto prototype = res->prototype();
|
const auto prototype = res->prototype();
|
||||||
return utils::ok(sql::query_result<sql::record>(std::forward<decltype(res)>(res), prototype));
|
return utils::ok(sql::query_result<sql::record>(std::forward<decltype(res)>(res), prototype));
|
||||||
});
|
});
|
||||||
@@ -55,9 +55,6 @@ utils::result<std::optional<sql::record>, utils::error> fetchable_query::fetch_o
|
|||||||
return utils::ok(std::optional{first.release()});
|
return utils::ok(std::optional{first.release()});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string fetchable_query::str(const sql::executor &exec) const {
|
|
||||||
return str(exec.dialect());
|
|
||||||
}
|
|
||||||
std::string fetchable_query::str(const sql::dialect &d) const {
|
std::string fetchable_query::str(const sql::dialect &d) const {
|
||||||
return compile(d).sql;
|
return compile(d).sql;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ query_add_foreign_key_constraint_intermediate query_add_key_constraint_intermedi
|
|||||||
|
|
||||||
executable_query query_alter_table_intermediate::add_constraint(const object::restriction &rest) {
|
executable_query query_alter_table_intermediate::add_constraint(const object::restriction &rest) {
|
||||||
context_->parts.push_back(std::make_unique<internal::query_add_constraint_part_by_constraint>(
|
context_->parts.push_back(std::make_unique<internal::query_add_constraint_part_by_constraint>(
|
||||||
table_constraint{rest.column_name(), rest.owner()->name(), rest.attribute().attributes().options(), rest.ref_table_name(), rest.ref_column_name()}
|
table_constraint{rest.column_name(), rest.owner()->name(), rest.options(), rest.ref_table_name(), rest.ref_column_name()}
|
||||||
));
|
));
|
||||||
|
|
||||||
return {context_};
|
return {context_};
|
||||||
@@ -47,7 +47,7 @@ query_add_key_constraint_intermediate query_alter_table_intermediate::add_constr
|
|||||||
|
|
||||||
executable_query query_alter_table_intermediate::drop_constraint(const object::restriction &rest) {
|
executable_query query_alter_table_intermediate::drop_constraint(const object::restriction &rest) {
|
||||||
context_->parts.push_back(std::make_unique<internal::query_drop_key_constraint_part_by_constraint>(
|
context_->parts.push_back(std::make_unique<internal::query_drop_key_constraint_part_by_constraint>(
|
||||||
table_constraint{rest.column_name(), rest.owner()->name(), rest.attribute().attributes().options(), rest.ref_table_name(), rest.ref_column_name()})
|
table_constraint{rest.column_name(), rest.owner()->name(), rest.options(), rest.ref_table_name(), rest.ref_column_name()})
|
||||||
);
|
);
|
||||||
return {context_};
|
return {context_};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ executable_query query_create_table_columns_intermediate::constraints(const std:
|
|||||||
std::list<table_constraint> constraints;
|
std::list<table_constraint> constraints;
|
||||||
for (const auto& restr : restrictions) {
|
for (const auto& restr : restrictions) {
|
||||||
if (restr.is_primary_key_constraint()) {
|
if (restr.is_primary_key_constraint()) {
|
||||||
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.attribute().attributes().options());
|
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.options());
|
||||||
} else if (restr.is_foreign_key_constraint()) {
|
} else if (restr.is_foreign_key_constraint()) {
|
||||||
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.attribute().attributes().options(), restr.ref_table_name(), restr.ref_column_name());
|
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.options(), restr.ref_table_name(), restr.ref_column_name());
|
||||||
} else if (restr.is_unique_constraint()) {
|
} else if (restr.is_unique_constraint()) {
|
||||||
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.attribute().attributes().options());
|
constraints.emplace_back(restr.column_name(), restr.owner()->name(), restr.options());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
context_->parts.push_back(std::make_unique<internal::query_create_table_constraints_part>(constraints));
|
context_->parts.push_back(std::make_unique<internal::query_create_table_constraints_part>(constraints));
|
||||||
@@ -58,10 +58,10 @@ executable_query query_create_table_columns_intermediate::constraints(const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
query_create_table_columns_intermediate query_create_table_intermediate::columns(const std::initializer_list<object::attribute> attributes) {
|
query_create_table_columns_intermediate query_create_table_intermediate::columns(const std::initializer_list<object::attribute> attributes) {
|
||||||
return columns(std::list(attributes));
|
return columns(std::vector(attributes));
|
||||||
}
|
}
|
||||||
|
|
||||||
query_create_table_columns_intermediate query_create_table_intermediate::columns(const std::list<object::attribute>& attributes) {
|
query_create_table_columns_intermediate query_create_table_intermediate::columns(const std::vector<object::attribute>& attributes) {
|
||||||
std::list<table_column> columns;
|
std::list<table_column> columns;
|
||||||
for (const auto& attr : attributes) {
|
for (const auto& attr : attributes) {
|
||||||
auto options = attr.attributes().options();
|
auto options = attr.attributes().options();
|
||||||
|
|||||||
@@ -3,26 +3,6 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace matador::query::internal {
|
namespace matador::query::internal {
|
||||||
|
|
||||||
// column_value_pair::column_value_pair(const std::string& name, utils::database_type value)
|
|
||||||
// : column_(name)
|
|
||||||
// , value_(std::move(value)) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// column_value_pair::column_value_pair(table_column col, utils::database_type value)
|
|
||||||
// : column_(std::move(col))
|
|
||||||
// , value_(std::move(value)) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// column_value_pair::column_value_pair(const char *name, utils::database_type value)
|
|
||||||
// : column_(name)
|
|
||||||
// , value_(std::move(value)) {
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// column_value_pair::column_value_pair( const char* name, utils::placeholder p )
|
|
||||||
// : column_(name)
|
|
||||||
// , value_(p) {}
|
|
||||||
|
|
||||||
column_value_pair::column_value_pair(table_column col, column_expression_ptr expression)
|
column_value_pair::column_value_pair(table_column col, column_expression_ptr expression)
|
||||||
: column_(std::move(col))
|
: column_(std::move(col))
|
||||||
, expression_(std::move(expression)){
|
, expression_(std::move(expression)){
|
||||||
@@ -32,10 +12,6 @@ const table_column &column_value_pair::col() const {
|
|||||||
return column_;
|
return column_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// const std::variant<utils::placeholder, utils::database_type>& column_value_pair::value() const {
|
|
||||||
// return value_;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const abstract_column_expression& column_value_pair::expression() const {
|
const abstract_column_expression& column_value_pair::expression() const {
|
||||||
return *expression_;
|
return *expression_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,6 @@
|
|||||||
#include "matador/query/internal/string_builder_utils.hpp"
|
#include "matador/query/internal/string_builder_utils.hpp"
|
||||||
#include "matador/query/internal/query_parts.hpp"
|
#include "matador/query/internal/query_parts.hpp"
|
||||||
|
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
|
||||||
|
|
||||||
#include "matador/sql/query_context.hpp"
|
#include "matador/sql/query_context.hpp"
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "matador/query/query_builder_exception.hpp"
|
#include "matador/query/query_builder_exception.hpp"
|
||||||
|
|
||||||
namespace matador::query {
|
namespace matador::query {
|
||||||
|
query_builder_exception::query_builder_exception(utils::error &&err)
|
||||||
|
: error_(std::move(err)) {}
|
||||||
|
|
||||||
query_builder_exception::query_builder_exception(const error_code error, std::string&& msg)
|
query_builder_exception::query_builder_exception(const error_code error, std::string&& msg)
|
||||||
: error_(error, msg)
|
: error_(error, msg)
|
||||||
|
|||||||
@@ -59,4 +59,19 @@ schema::iterator schema::insert_table(const std::type_index &ti, const object::r
|
|||||||
}
|
}
|
||||||
return schema_nodes_.insert({ti, schema_node{table(node.name(), columns), std::move(pk_generator), node}}).first;
|
return schema_nodes_.insert({ti, schema_node{table(node.name(), columns), std::move(pk_generator), node}}).first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
basic_schema::iterator schema::insert_relation_table(const std::type_index &ti, const object::repository_node &node) {
|
||||||
|
std::vector<table_column> columns;
|
||||||
|
const auto* attr = node.info().object()->join_attribute();
|
||||||
|
if (attr == nullptr) {
|
||||||
|
return schema_nodes_.end();
|
||||||
|
}
|
||||||
|
columns.emplace_back(nullptr, attr->name(), attr->type(), attr->attributes());
|
||||||
|
attr = node.info().object()->inverse_join_attribute();
|
||||||
|
if (attr == nullptr) {
|
||||||
|
return schema_nodes_.end();
|
||||||
|
}
|
||||||
|
columns.emplace_back(nullptr, attr->name(), attr->type(), attr->attributes());
|
||||||
|
return schema_nodes_.insert({ti, schema_node{table(node.name(), columns, node.info().object()->join_attribute()->name(), node.info().object()->inverse_join_attribute()->name()), nullptr, node}}).first;
|
||||||
|
}
|
||||||
} // namespace matador::query
|
} // namespace matador::query
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
#include "matador/query/schema_utils.hpp"
|
||||||
|
#include "matador/query/query.hpp"
|
||||||
|
#include "matador/query/criteria.hpp"
|
||||||
|
|
||||||
|
using namespace matador::utils;
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
query_contexts to_query_contexts(const schema_node& node, const sql::dialect &d) {
|
||||||
|
query_contexts queries;
|
||||||
|
|
||||||
|
// SELECT all
|
||||||
|
queries.select_all = select(node.table())
|
||||||
|
.from(node.name())
|
||||||
|
.compile(d);
|
||||||
|
if (node.table().has_primary_key()) {
|
||||||
|
// SELECT one
|
||||||
|
queries.select_one = select(node.table())
|
||||||
|
.from(node.name())
|
||||||
|
.where(*node.table().primary_key_column() == _)
|
||||||
|
.compile(d);
|
||||||
|
// UPDATE one
|
||||||
|
auto update_set = query::update(node.table());
|
||||||
|
for (const auto &col: node.table().columns()) {
|
||||||
|
update_set.set(col, _);
|
||||||
|
}
|
||||||
|
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
|
||||||
|
.compile(d);
|
||||||
|
// DELETE one
|
||||||
|
queries.delete_one = query::remove()
|
||||||
|
.from(node.name())
|
||||||
|
.where(*node.table().primary_key_column() == _)
|
||||||
|
.compile(d);
|
||||||
|
} else {
|
||||||
|
queries.delete_one = query::remove()
|
||||||
|
.from(node.name())
|
||||||
|
.where(*node.table().join_column() == _ && *node.table().inverse_join_column() == _)
|
||||||
|
.compile(d);
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
if (node.table().has_primary_key() && node.pk_generator().type() == generator_type::Identity) {
|
||||||
|
queries.insert = query::insert()
|
||||||
|
.into(node.name(), columns)
|
||||||
|
.values(generator::placeholders(columns.size()))
|
||||||
|
.returning(node.table().primary_key_column()->as(node.table().primary_key_column()->column_name()))
|
||||||
|
.compile(d);
|
||||||
|
} else {
|
||||||
|
queries.insert = query::insert()
|
||||||
|
.into(node.name(), columns)
|
||||||
|
.values(generator::placeholders(columns.size()))
|
||||||
|
.compile(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return queries;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -83,7 +83,7 @@ void select_query_builder::append_join(const table_column &left, const table_col
|
|||||||
using namespace matador::query;
|
using namespace matador::query;
|
||||||
entity_query_data_.joins.push_back({
|
entity_query_data_.joins.push_back({
|
||||||
right.table(),
|
right.table(),
|
||||||
std::make_unique<binary_column_criteria>(left, binary_operator::EQUALS, right)
|
std::make_unique<binary_column_criteria>(left, binary_operator::Equals, right)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
#include "matador/sql/backend_provider.hpp"
|
#include "matador/sql/backend_provider.hpp"
|
||||||
#include "matador/sql/dialect.hpp"
|
#include "matador/sql/dialect.hpp"
|
||||||
|
|
||||||
#include "matador/query/query.hpp"
|
|
||||||
#include "matador/query/generator.hpp"
|
|
||||||
#include "matador/query/basic_schema.hpp"
|
#include "matador/query/basic_schema.hpp"
|
||||||
|
#include "matador/query/schema_utils.hpp"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
@@ -18,55 +17,12 @@ 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))
|
||||||
|
, object_cache_(ctx.bus)
|
||||||
, schema_(scm)
|
, 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_contexts queries;
|
query_contexts queries = to_query_contexts(node, dialect_);
|
||||||
|
|
||||||
// SELECT all
|
|
||||||
queries.select_all = select(node.table())
|
|
||||||
.from(node.name())
|
|
||||||
.compile(dialect_);
|
|
||||||
if (node.table().has_primary_key()) {
|
|
||||||
// SELECT one
|
|
||||||
queries.select_one = select(node.table())
|
|
||||||
.from(node.name())
|
|
||||||
.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(dialect_);
|
|
||||||
// UPDATE one
|
|
||||||
auto update_set = query::update(node.table());
|
|
||||||
for (const auto &col: node.table().columns()) {
|
|
||||||
update_set.set(col, _);
|
|
||||||
}
|
|
||||||
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(dialect_);
|
|
||||||
// DELETE one
|
|
||||||
queries.delete_one = query::remove()
|
|
||||||
.from(node.name())
|
|
||||||
.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(dialect_);
|
|
||||||
}
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
if (node.pk_generator().type() == utils::generator_type::Identity) {
|
|
||||||
queries.insert = query::insert()
|
|
||||||
.into(node.name(), columns)
|
|
||||||
.values(generator::placeholders(columns.size()))
|
|
||||||
.returning(node.table().primary_key_column()->as(node.table().primary_key_column()->column_name()))
|
|
||||||
.compile(dialect_);
|
|
||||||
} else {
|
|
||||||
queries.insert = query::insert()
|
|
||||||
.into(node.name(), columns)
|
|
||||||
.values(generator::placeholders(columns.size()))
|
|
||||||
.compile(dialect_);
|
|
||||||
}
|
|
||||||
|
|
||||||
queries.insert.resolver = resolver_service_;
|
queries.insert.resolver = resolver_service_;
|
||||||
queries.update_one.resolver = resolver_service_;
|
queries.update_one.resolver = resolver_service_;
|
||||||
|
|||||||
@@ -9,13 +9,17 @@ table::table(const char* name)
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
table::table(const std::string& name)
|
table::table(const std::string& name)
|
||||||
: table(name, name, {}) {}
|
: table(name, name, {}, {}, {}) {}
|
||||||
|
|
||||||
table::table(const std::string& name, const std::vector<table_column> &columns)
|
table::table(const std::string& name, const std::vector<table_column> &columns)
|
||||||
: table(name, name, columns) {
|
: table(name, name, columns, {}, {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
table::table(std::string name, std::string alias, const std::vector<table_column> &columns)
|
table::table(const std::string& name, const std::vector<table_column>& columns, const std::string& join_column, const std::string& inverse_join_column)
|
||||||
|
: table(name, name, columns, join_column, inverse_join_column) {
|
||||||
|
}
|
||||||
|
|
||||||
|
table::table(std::string name, std::string alias, const std::vector<table_column> &columns, const std::string& join_column, const std::string& inverse_join_column)
|
||||||
: name_(std::move(name))
|
: name_(std::move(name))
|
||||||
, alias_(std::move(alias))
|
, alias_(std::move(alias))
|
||||||
, columns_(columns) {
|
, columns_(columns) {
|
||||||
@@ -24,6 +28,12 @@ table::table(std::string name, std::string alias, const std::vector<table_column
|
|||||||
if (columns_[i].is_primary_key()) {
|
if (columns_[i].is_primary_key()) {
|
||||||
pk_column_index_ = i;
|
pk_column_index_ = i;
|
||||||
}
|
}
|
||||||
|
if (columns_[i].column_name() == join_column) {
|
||||||
|
join_column_index_ = i;
|
||||||
|
}
|
||||||
|
if (columns_[i].column_name() == inverse_join_column) {
|
||||||
|
inverse_join_column_index_ = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +42,9 @@ table::table(const table &other)
|
|||||||
, alias_(other.alias_)
|
, alias_(other.alias_)
|
||||||
, schema_name_(other.schema_name_)
|
, schema_name_(other.schema_name_)
|
||||||
, columns_(other.columns_)
|
, columns_(other.columns_)
|
||||||
, pk_column_index_(other.pk_column_index_){
|
, pk_column_index_(other.pk_column_index_)
|
||||||
|
, join_column_index_(other.join_column_index_)
|
||||||
|
, inverse_join_column_index_(other.inverse_join_column_index_) {
|
||||||
for (auto &col : columns_) {
|
for (auto &col : columns_) {
|
||||||
col.table(this);
|
col.table(this);
|
||||||
}
|
}
|
||||||
@@ -54,6 +66,8 @@ table & table::operator=(table &&other) noexcept {
|
|||||||
alias_ = std::move(other.alias_);
|
alias_ = std::move(other.alias_);
|
||||||
columns_ = std::move(other.columns_);
|
columns_ = std::move(other.columns_);
|
||||||
pk_column_index_ = other.pk_column_index_;
|
pk_column_index_ = other.pk_column_index_;
|
||||||
|
join_column_index_ = other.join_column_index_;
|
||||||
|
inverse_join_column_index_ = other.inverse_join_column_index_;
|
||||||
for (auto &col : columns_) {
|
for (auto &col : columns_) {
|
||||||
col.table(this);
|
col.table(this);
|
||||||
}
|
}
|
||||||
@@ -65,7 +79,7 @@ bool table::operator==(const table& x) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
table table::as(const std::string &alias) const {
|
table table::as(const std::string &alias) const {
|
||||||
return { name_, alias, columns_ };
|
return { name_, alias, columns_, join_column_index_ > -1 ? join_column()->name() : std::string{}, inverse_join_column_index_ > -1 ? inverse_join_column()->name() : std::string{}};
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string & table::table_name() const {
|
const std::string & table::table_name() const {
|
||||||
@@ -121,11 +135,11 @@ const table_column* table::primary_key_column() const {
|
|||||||
return pk_column_index_ > -1 ? &columns_.at(pk_column_index_) : nullptr;
|
return pk_column_index_ > -1 ? &columns_.at(pk_column_index_) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &table::join_column_name() const {
|
const table_column* table::join_column() const {
|
||||||
return join_column_name_;
|
return join_column_index_ > -1 ? &columns_.at(join_column_index_) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string &table::inverse_join_column_name() const {
|
const table_column* table::inverse_join_column() const {
|
||||||
return inverse_join_column_name_;
|
return inverse_join_column_index_ > -1 ? &columns_.at(inverse_join_column_index_) : nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ void QueryFixture::check_table_not_exists(const std::string &table_name) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void QueryFixture::drop_table_if_exists(const std::string &table_name) const {
|
void QueryFixture::drop_table_if_exists(const std::string &table_name) const {
|
||||||
const auto result = db.exists(table_name).and_then([&table_name, this](const bool exists) {
|
const auto result = db.exists(table_name)
|
||||||
|
.and_then([&table_name, this](const bool exists) -> utils::result<bool, utils::error> {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
auto res = query::drop()
|
auto res = query::drop()
|
||||||
.table(table_name)
|
.table(table_name)
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ void SequenceFixture::check_sequence_not_exists(const std::string& sequence_name
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SequenceFixture::drop_sequence_if_exists(const std::string& sequence_name) const {
|
void SequenceFixture::drop_sequence_if_exists(const std::string& sequence_name) const {
|
||||||
const auto result = db.sequence_exists(sequence_name).and_then([&sequence_name, this](const bool exists) {
|
const auto result = db.sequence_exists(sequence_name).
|
||||||
|
and_then([&sequence_name, this](const bool exists) -> utils::result<bool, utils::error> {
|
||||||
if (exists) {
|
if (exists) {
|
||||||
auto res = query::drop()
|
auto res = query::drop()
|
||||||
.sequence(sequence_name)
|
.sequence(sequence_name)
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
#include "catch2/catch_test_macros.hpp"
|
||||||
|
|
||||||
|
#include "SessionFixture.hpp"
|
||||||
|
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/session.hpp"
|
||||||
|
|
||||||
|
#include "models/author.hpp"
|
||||||
|
#include "models/book.hpp"
|
||||||
|
|
||||||
|
using namespace matador::test;
|
||||||
|
using namespace matador::query;
|
||||||
|
using namespace matador::object;
|
||||||
|
|
||||||
|
namespace matador::test {
|
||||||
|
template<typename AuthorType>
|
||||||
|
void validate_author_state(const object_ptr<AuthorType>& ptr, object_state expected_state) {
|
||||||
|
REQUIRE(ptr.is_state(expected_state));
|
||||||
|
for (auto &b: ptr->books) {
|
||||||
|
REQUIRE(b.is_state(expected_state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace matador::utils {
|
||||||
|
template < typename ValueType >
|
||||||
|
std::ostream& operator<<(std::ostream& os, const result<ValueType, error>& value) {
|
||||||
|
if (value) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
return os << "Error: " << value.err();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<(std::ostream& os, const result<void, error>& value) {
|
||||||
|
if (value) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
return os << "Error: " << value.err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(SessionFixture, "Test delete object with has many relation", "[session][delete][has_many]") {
|
||||||
|
const auto result = schema.attach<book>("books")
|
||||||
|
.and_then( [this] { return schema.attach<author>("authors"); } )
|
||||||
|
.and_then([this] { return schema.create(db); } );
|
||||||
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
|
session ses({bus, connection::dns, 4}, schema);
|
||||||
|
|
||||||
|
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
||||||
|
|
||||||
|
s_king->books.push_back(make_object<book>(2, "Carrie", nullobj, 1974));
|
||||||
|
s_king->books.push_back(make_object<book>(3, "The Shining", nullobj, 1977));
|
||||||
|
s_king->books.push_back(make_object<book>(4, "It", nullobj, 1986));
|
||||||
|
s_king->books.push_back(make_object<book>(5, "Misery", nullobj, 1987));
|
||||||
|
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", nullobj, 1982));
|
||||||
|
|
||||||
|
validate_author_state(s_king, object_state::Transient);
|
||||||
|
auto res = ses.insert(s_king);
|
||||||
|
REQUIRE(res);
|
||||||
|
validate_author_state(s_king, object_state::Persistent);
|
||||||
|
|
||||||
|
auto author_result = ses.find<author>(s_king->id);
|
||||||
|
REQUIRE(author_result);
|
||||||
|
REQUIRE(author_result->is_persistent());
|
||||||
|
REQUIRE(author_result.value()->books.size() == 5);
|
||||||
|
|
||||||
|
const auto id = s_king->id;
|
||||||
|
auto del_res = ses.remove(s_king);
|
||||||
|
REQUIRE(del_res);
|
||||||
|
|
||||||
|
author_result = ses.find<author>(id);
|
||||||
|
REQUIRE_FALSE(author_result);
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
#include "catch2/catch_test_macros.hpp"
|
||||||
|
|
||||||
|
#include "SessionFixture.hpp"
|
||||||
|
|
||||||
|
#include "connection.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/session.hpp"
|
||||||
|
|
||||||
|
#include "models/recipe.hpp"
|
||||||
|
|
||||||
|
using namespace matador::test;
|
||||||
|
using namespace matador::query;
|
||||||
|
using namespace matador::object;
|
||||||
|
|
||||||
|
namespace matador::test {
|
||||||
|
template<typename AuthorType>
|
||||||
|
void validate_author_state(const object_ptr<AuthorType>& ptr, object_state expected_state) {
|
||||||
|
REQUIRE(ptr.is_state(expected_state));
|
||||||
|
for (auto &b: ptr->books) {
|
||||||
|
REQUIRE(b.is_state(expected_state));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(SessionFixture, "Test delete object with has many to many relation", "[session][delete][has_many_to_many]") {
|
||||||
|
auto result = schema.attach<recipe_sequence>("recipes")
|
||||||
|
.and_then( [this] { return schema.attach<ingredient_sequence>("ingredients"); } )
|
||||||
|
.and_then([this] { return schema.create(db); } );
|
||||||
|
|
||||||
|
session ses({bus, connection::dns, 4}, schema);
|
||||||
|
|
||||||
|
std::vector ingredients {
|
||||||
|
make_object<ingredient_sequence>("Apple"),
|
||||||
|
make_object<ingredient_sequence>("Strawberry"),
|
||||||
|
make_object<ingredient_sequence>("Pineapple"),
|
||||||
|
make_object<ingredient_sequence>("Sugar"),
|
||||||
|
make_object<ingredient_sequence>("Flour"),
|
||||||
|
make_object<ingredient_sequence>("Butter"),
|
||||||
|
make_object<ingredient_sequence>("Beans")
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector recipes {
|
||||||
|
make_object<recipe_sequence>("Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}),
|
||||||
|
make_object<recipe_sequence>("Strawberry Cake", std::vector{ingredients[5], ingredients[6]}),
|
||||||
|
make_object<recipe_sequence>("Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &r: recipes) {
|
||||||
|
REQUIRE(r.is_transient());
|
||||||
|
auto res = ses.insert(r);
|
||||||
|
REQUIRE(res.is_ok());
|
||||||
|
REQUIRE(res->is_persistent());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto recipe_result = ses.find<recipe_sequence>(1);
|
||||||
|
REQUIRE(recipe_result.is_ok());
|
||||||
|
REQUIRE(recipe_result->is_persistent());
|
||||||
|
REQUIRE(recipe_result.value()->ingredients.size() == 3);
|
||||||
|
|
||||||
|
const auto ing_result = ses.find<ingredient_sequence>(ingredients[0]->id);
|
||||||
|
REQUIRE(ing_result.is_ok());
|
||||||
|
REQUIRE(ing_result.value()->recipes.size() == 2);
|
||||||
|
|
||||||
|
auto del_res = ses.remove(recipes[0]);
|
||||||
|
REQUIRE(del_res);
|
||||||
|
|
||||||
|
recipe_result = ses.find<recipe_sequence>(1);
|
||||||
|
REQUIRE_FALSE(recipe_result);
|
||||||
|
}
|
||||||
@@ -25,7 +25,8 @@ TEST_CASE_METHOD(SessionFixture, "Test insert object with belongs to relation wi
|
|||||||
const auto carrie = make_object<book_identity>("Carrie", s_king, 1974);
|
const auto carrie = make_object<book_identity>("Carrie", s_king, 1974);
|
||||||
|
|
||||||
REQUIRE(carrie.is_transient());
|
REQUIRE(carrie.is_transient());
|
||||||
REQUIRE(ses.insert(carrie).is_ok());
|
const auto res = ses.insert(carrie);
|
||||||
|
REQUIRE(res.is_ok());
|
||||||
REQUIRE(carrie.is_persistent());
|
REQUIRE(carrie.is_persistent());
|
||||||
|
|
||||||
auto found_author = ses.find<author_identity>(s_king->id);
|
auto found_author = ses.find<author_identity>(s_king->id);
|
||||||
|
|||||||
@@ -61,6 +61,16 @@ void validate_author_state(const object_ptr<AuthorType>& ptr, object_state expec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace matador::utils {
|
||||||
|
template < typename ValueType >
|
||||||
|
std::ostream& operator<<(std::ostream& os, const result<ValueType, error>& value) {
|
||||||
|
if (value) {
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
return os << "Error: " << value.err();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation", "[session][insert][has_many]") {
|
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation", "[session][insert][has_many]") {
|
||||||
const auto result = schema.attach<book>("books")
|
const auto result = schema.attach<book>("books")
|
||||||
.and_then( [this] { return schema.attach<author>("authors"); } )
|
.and_then( [this] { return schema.attach<author>("authors"); } )
|
||||||
@@ -69,6 +79,7 @@ TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation", "[
|
|||||||
|
|
||||||
session ses({bus, connection::dns, 4}, schema);
|
session ses({bus, connection::dns, 4}, schema);
|
||||||
|
|
||||||
|
{
|
||||||
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
||||||
|
|
||||||
s_king->books.push_back(make_object<book>(2, "Carrie", nullobj, 1974));
|
s_king->books.push_back(make_object<book>(2, "Carrie", nullobj, 1974));
|
||||||
@@ -79,8 +90,19 @@ TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation", "[
|
|||||||
|
|
||||||
validate_author_state(s_king, object_state::Transient);
|
validate_author_state(s_king, object_state::Transient);
|
||||||
auto res = ses.insert(s_king);
|
auto res = ses.insert(s_king);
|
||||||
REQUIRE(res.is_ok());
|
REQUIRE(res);
|
||||||
validate_author_state(s_king, object_state::Persistent);
|
validate_author_state(s_king, object_state::Persistent);
|
||||||
|
|
||||||
|
auto author_result = ses.find<author>(s_king->id);
|
||||||
|
REQUIRE(author_result);
|
||||||
|
REQUIRE(author_result->is_persistent());
|
||||||
|
REQUIRE(author_result.value()->books.size() == 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto author_result = ses.find<author>(1);
|
||||||
|
REQUIRE(author_result);
|
||||||
|
REQUIRE(author_result->is_persistent());
|
||||||
|
REQUIRE(author_result.value()->books.size() == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation with identity", "[session][insert][has_many][identity]") {
|
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many relation with identity", "[session][insert][has_many][identity]") {
|
||||||
|
|||||||
@@ -14,66 +14,6 @@ using namespace matador::object;
|
|||||||
using namespace matador::test;
|
using namespace matador::test;
|
||||||
using namespace matador::query::meta;
|
using namespace matador::query::meta;
|
||||||
|
|
||||||
namespace matador::test {
|
|
||||||
template<const utils::primary_key_attribute &PkAttribute>
|
|
||||||
struct recipe_pk_generator;
|
|
||||||
|
|
||||||
template<const utils::primary_key_attribute &PkAttribute>
|
|
||||||
struct ingredient_pk_generator {
|
|
||||||
unsigned int id{};
|
|
||||||
std::string name;
|
|
||||||
collection<object_ptr<recipe_pk_generator<PkAttribute>>> recipes{};
|
|
||||||
|
|
||||||
ingredient_pk_generator() = default;
|
|
||||||
|
|
||||||
explicit ingredient_pk_generator(std::string name)
|
|
||||||
: name(std::move(name)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
ingredient_pk_generator(std::string name, std::vector<object_ptr<recipe_pk_generator<PkAttribute>>> recps)
|
|
||||||
: name(std::move(name))
|
|
||||||
, recipes(std::move(recps)){}
|
|
||||||
|
|
||||||
template<class Operator>
|
|
||||||
void process(Operator &op) {
|
|
||||||
namespace field = matador::access;
|
|
||||||
field::primary_key(op, "id", id, PkAttribute);
|
|
||||||
field::attribute(op, "name", name, UniqueVarChar255);
|
|
||||||
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<const utils::primary_key_attribute &PkAttribute>
|
|
||||||
struct recipe_pk_generator {
|
|
||||||
unsigned int id{};
|
|
||||||
std::string name;
|
|
||||||
collection<object_ptr<ingredient_pk_generator<PkAttribute>>> ingredients{};
|
|
||||||
|
|
||||||
recipe_pk_generator() = default;
|
|
||||||
explicit recipe_pk_generator(std::string name)
|
|
||||||
: name(std::move(name)) {
|
|
||||||
}
|
|
||||||
recipe_pk_generator(std::string name, std::vector<object_ptr<ingredient_pk_generator<PkAttribute>>> ings)
|
|
||||||
: name(std::move(name))
|
|
||||||
, ingredients(std::move(ings)){}
|
|
||||||
|
|
||||||
template<class Operator>
|
|
||||||
void process(Operator &op) {
|
|
||||||
namespace field = matador::access;
|
|
||||||
field::primary_key(op, "id", id, PkAttribute);
|
|
||||||
field::attribute(op, "name", name, UniqueVarChar255);
|
|
||||||
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using ingredient_identity = ingredient_pk_generator<utils::Identity>;
|
|
||||||
using ingredient_table = ingredient_pk_generator<utils::Table>;
|
|
||||||
using ingredient_sequence = ingredient_pk_generator<utils::Sequence>;
|
|
||||||
using recipe_identity = recipe_pk_generator<utils::Identity>;
|
|
||||||
using recipe_table = recipe_pk_generator<utils::Table>;
|
|
||||||
using recipe_sequence = recipe_pk_generator<utils::Sequence>;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation", "[session][insert][has_many_to_many]") {
|
TEST_CASE_METHOD(SessionFixture, "Test insert object with has many to many relation", "[session][insert][has_many_to_many]") {
|
||||||
auto result = schema.attach<recipe>("recipes")
|
auto result = schema.attach<recipe>("recipes")
|
||||||
.and_then( [this] { return schema.attach<ingredient>("ingredients"); } )
|
.and_then( [this] { return schema.attach<ingredient>("ingredients"); } )
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
|||||||
add_executable(CoreTests
|
add_executable(CoreTests
|
||||||
../backends/SchemaFixture.hpp
|
../backends/SchemaFixture.hpp
|
||||||
logger/LoggerTest.cpp
|
logger/LoggerTest.cpp
|
||||||
|
object/CollectionTest.cpp
|
||||||
object/ObjectCacheTest.cpp
|
object/ObjectCacheTest.cpp
|
||||||
object/ObjectTest.cpp
|
object/ObjectTest.cpp
|
||||||
object/PrimaryKeyResolverTest.cpp
|
object/PrimaryKeyResolverTest.cpp
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "matador/object/collection.hpp"
|
||||||
|
#include "matador/object/object.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace matador::object;
|
||||||
|
|
||||||
|
TEST_CASE("Test collection", "[collection]") {
|
||||||
|
collection<int> ints{{1,2,3,4}};
|
||||||
|
|
||||||
|
for (auto const& i : ints) {
|
||||||
|
std::cout << i << std::endl ;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
#include "matador/object/object_cache.hpp"
|
#include "matador/object/object_cache.hpp"
|
||||||
#include "matador/object/object_resolver.hpp"
|
#include "matador/object/object_resolver.hpp"
|
||||||
|
|
||||||
#include "matador/utils/identifier.hpp"
|
#include "matador/utils/identifier.hpp"
|
||||||
|
#include "matador/utils/message_bus.hpp"
|
||||||
|
|
||||||
#include "../test/models/person.hpp"
|
#include "../test/models/person.hpp"
|
||||||
|
|
||||||
@@ -57,8 +59,9 @@ private:
|
|||||||
};
|
};
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST_CASE("object_cache: acquire_proxy returns the same proxy instance across threads", "[object][cache][threadsafe]") {
|
TEST_CASE("ObjectCache: acquire_proxy returns the same proxy instance across threads", "[object][cache][threadsafe]") {
|
||||||
matador::object::object_cache cache;
|
matador::utils::message_bus bus;
|
||||||
|
matador::object::object_cache cache(bus);
|
||||||
const matador::utils::identifier id{123};
|
const matador::utils::identifier id{123};
|
||||||
|
|
||||||
std::atomic_int calls{0};
|
std::atomic_int calls{0};
|
||||||
@@ -88,8 +91,9 @@ TEST_CASE("object_cache: acquire_proxy returns the same proxy instance across th
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("object_cache: attach_entity makes is_loaded/get_entity reflect presence", "[object][cache]") {
|
TEST_CASE("ObjectCache: attach_entity makes is_loaded/get_entity reflect presence", "[object][cache]") {
|
||||||
matador::object::object_cache cache;
|
matador::utils::message_bus bus;
|
||||||
|
matador::object::object_cache cache(bus);
|
||||||
const matador::utils::identifier id{42};
|
const matador::utils::identifier id{42};
|
||||||
|
|
||||||
REQUIRE_FALSE(cache.is_loaded<person>(id));
|
REQUIRE_FALSE(cache.is_loaded<person>(id));
|
||||||
@@ -106,8 +110,9 @@ TEST_CASE("object_cache: attach_entity makes is_loaded/get_entity reflect presen
|
|||||||
REQUIRE(got->name == "hans");
|
REQUIRE(got->name == "hans");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("object_cache: erase invalidates existing proxies", "[object][cache]") {
|
TEST_CASE("ObjectCache: erase invalidates existing proxies", "[object][cache]") {
|
||||||
matador::object::object_cache cache;
|
matador::utils::message_bus bus;
|
||||||
|
matador::object::object_cache cache(bus);
|
||||||
const matador::utils::identifier id{9};
|
const matador::utils::identifier id{9};
|
||||||
|
|
||||||
std::atomic_int calls{0};
|
std::atomic_int calls{0};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
#include "../test/models/author.hpp"
|
#include "../test/models/author.hpp"
|
||||||
#include "../test/models/book.hpp"
|
#include "../test/models/book.hpp"
|
||||||
|
|
||||||
TEST_CASE("Generate object from type", "[object][generate]") {
|
TEST_CASE("Object: Generate object from type", "[object][generate]") {
|
||||||
using namespace matador;
|
using namespace matador;
|
||||||
object::repository repo;
|
object::repository repo;
|
||||||
auto result = repo.attach<test::author>("authors");
|
auto result = repo.attach<test::author>("authors");
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
TEST_CASE("Test create identifier", "[identifier][create]") {
|
TEST_CASE("Identifier: Test create identifier", "[identifier][create]") {
|
||||||
const identifier id;
|
const identifier id;
|
||||||
|
|
||||||
REQUIRE(id.is_null());
|
REQUIRE(id.is_null());
|
||||||
@@ -16,7 +16,7 @@ TEST_CASE("Test create identifier", "[identifier][create]") {
|
|||||||
REQUIRE(id.str() == "null");
|
REQUIRE(id.str() == "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test assign value to identifier", "[identifier][assign]") {
|
TEST_CASE("Identifier: Test assign value to identifier", "[identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
REQUIRE(id.is_null());
|
REQUIRE(id.is_null());
|
||||||
@@ -48,7 +48,7 @@ TEST_CASE("Test assign value to identifier", "[identifier][assign]") {
|
|||||||
// REQUIRE(id == identifier{"UniqueId"});
|
// REQUIRE(id == identifier{"UniqueId"});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test compare identifier", "[identifier][compare]") {
|
TEST_CASE("Identifier: Test compare identifier", "[identifier][compare]") {
|
||||||
identifier id1{6}, id2{7};
|
identifier id1{6}, id2{7};
|
||||||
|
|
||||||
REQUIRE(id1 != id2);
|
REQUIRE(id1 != id2);
|
||||||
@@ -68,7 +68,7 @@ identifier create(const int id) {
|
|||||||
return identifier{id};
|
return identifier{id};
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test copy identifier" "[identifier][copy]") {
|
TEST_CASE("Identifier: Test copy identifier" "[identifier][copy]") {
|
||||||
identifier id1{"Unique"};
|
identifier id1{"Unique"};
|
||||||
REQUIRE(id1.is_valid());
|
REQUIRE(id1.is_valid());
|
||||||
REQUIRE(id1.str() == "Unique");
|
REQUIRE(id1.str() == "Unique");
|
||||||
@@ -92,7 +92,7 @@ TEST_CASE("Test copy identifier" "[identifier][copy]") {
|
|||||||
id3 = id1;
|
id3 = id1;
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test move identifier", "[identifier][move]") {
|
TEST_CASE("Identifier: Test move identifier", "[identifier][move]") {
|
||||||
identifier id1{6};
|
identifier id1{6};
|
||||||
|
|
||||||
REQUIRE(id1.is_integer());
|
REQUIRE(id1.is_integer());
|
||||||
@@ -103,7 +103,7 @@ TEST_CASE("Test move identifier", "[identifier][move]") {
|
|||||||
REQUIRE(id2.is_integer());
|
REQUIRE(id2.is_integer());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assignment from integer types", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assignment from integer types", "[utils][identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
id = int8_t{-5};
|
id = int8_t{-5};
|
||||||
@@ -125,7 +125,7 @@ TEST_CASE("identifier assignment from integer types", "[utils][identifier][assig
|
|||||||
REQUIRE(id.is_valid());
|
REQUIRE(id.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assignment from string type", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assignment from string type", "[utils][identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
id = std::string{"hello"};
|
id = std::string{"hello"};
|
||||||
@@ -140,7 +140,7 @@ TEST_CASE("identifier assignment from string type", "[utils][identifier][assign]
|
|||||||
REQUIRE_FALSE(id.is_valid());
|
REQUIRE_FALSE(id.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assignment from const char*", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assignment from const char*", "[utils][identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
id = "world";
|
id = "world";
|
||||||
@@ -155,7 +155,7 @@ TEST_CASE("identifier assignment from const char*", "[utils][identifier][assign]
|
|||||||
REQUIRE_FALSE(id.is_valid());
|
REQUIRE_FALSE(id.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assignment from nullptr", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assignment from nullptr", "[utils][identifier][assign]") {
|
||||||
identifier id{42};
|
identifier id{42};
|
||||||
|
|
||||||
REQUIRE(id.is_valid());
|
REQUIRE(id.is_valid());
|
||||||
@@ -168,7 +168,7 @@ TEST_CASE("identifier assignment from nullptr", "[utils][identifier][assign]") {
|
|||||||
REQUIRE_FALSE(id.is_valid());
|
REQUIRE_FALSE(id.is_valid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier reassignment between types", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier reassignment between types", "[utils][identifier][assign]") {
|
||||||
identifier id{uint64_t{7}};
|
identifier id{uint64_t{7}};
|
||||||
|
|
||||||
REQUIRE(id.is_integer());
|
REQUIRE(id.is_integer());
|
||||||
@@ -189,7 +189,7 @@ TEST_CASE("identifier reassignment between types", "[utils][identifier][assign]"
|
|||||||
REQUIRE(id.str() == "null");
|
REQUIRE(id.str() == "null");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier as() returns exact integer type", "[utils][identifier][as]") {
|
TEST_CASE("Identifier: Identifier as() returns exact integer type", "[utils][identifier][as]") {
|
||||||
identifier id{int32_t{42}};
|
identifier id{int32_t{42}};
|
||||||
|
|
||||||
const auto as_i32 = id.as<int32_t>();
|
const auto as_i32 = id.as<int32_t>();
|
||||||
@@ -200,7 +200,7 @@ TEST_CASE("identifier as() returns exact integer type", "[utils][identifier][as]
|
|||||||
REQUIRE(as_i64.is_error());
|
REQUIRE(as_i64.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier as() returns exact string type", "[utils][identifier][as]") {
|
TEST_CASE("Identifier: Identifier as() returns exact string type", "[utils][identifier][as]") {
|
||||||
identifier id{std::string{"hello"}};
|
identifier id{std::string{"hello"}};
|
||||||
|
|
||||||
const auto as_string = id.as<std::string>();
|
const auto as_string = id.as<std::string>();
|
||||||
@@ -211,7 +211,7 @@ TEST_CASE("identifier as() returns exact string type", "[utils][identifier][as]"
|
|||||||
REQUIRE(as_i32.is_error());
|
REQUIRE(as_i32.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier as() returns null type failure for null identifier", "[utils][identifier][as]") {
|
TEST_CASE("Identifier: Identifier as() returns null type failure for null identifier", "[utils][identifier][as]") {
|
||||||
identifier id{nullptr};
|
identifier id{nullptr};
|
||||||
|
|
||||||
const auto as_i32 = id.as<int32_t>();
|
const auto as_i32 = id.as<int32_t>();
|
||||||
@@ -221,7 +221,7 @@ TEST_CASE("identifier as() returns null type failure for null identifier", "[uti
|
|||||||
REQUIRE(as_string.is_error());
|
REQUIRE(as_string.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier convert() converts between integer types", "[utils][identifier][convert]") {
|
TEST_CASE("Identifier: Identifier convert() converts between integer types", "[utils][identifier][convert]") {
|
||||||
identifier id{int32_t{123}};
|
identifier id{int32_t{123}};
|
||||||
|
|
||||||
const auto as_i64 = id.convert<int64_t>();
|
const auto as_i64 = id.convert<int64_t>();
|
||||||
@@ -233,21 +233,21 @@ TEST_CASE("identifier convert() converts between integer types", "[utils][identi
|
|||||||
REQUIRE(*as_u8 == 123);
|
REQUIRE(*as_u8 == 123);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier convert() rejects out of range integer conversion", "[utils][identifier][convert]") {
|
TEST_CASE("Identifier: Identifier convert() rejects out of range integer conversion", "[utils][identifier][convert]") {
|
||||||
identifier id{int32_t{300}};
|
identifier id{int32_t{300}};
|
||||||
|
|
||||||
const auto as_u8 = id.convert<uint8_t>();
|
const auto as_u8 = id.convert<uint8_t>();
|
||||||
REQUIRE(as_u8.is_error());
|
REQUIRE(as_u8.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier convert() rejects negative to unsigned conversion", "[utils][identifier][convert]") {
|
TEST_CASE("Identifier: Identifier convert() rejects negative to unsigned conversion", "[utils][identifier][convert]") {
|
||||||
identifier id{int32_t{-1}};
|
identifier id{int32_t{-1}};
|
||||||
|
|
||||||
const auto as_u32 = id.convert<uint32_t>();
|
const auto as_u32 = id.convert<uint32_t>();
|
||||||
REQUIRE(as_u32.is_error());
|
REQUIRE(as_u32.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier convert() rejects non-integer source types", "[utils][identifier][convert]") {
|
TEST_CASE("Identifier: Identifier convert() rejects non-integer source types", "[utils][identifier][convert]") {
|
||||||
identifier id{std::string{"123"}};
|
identifier id{std::string{"123"}};
|
||||||
|
|
||||||
const auto as_i32 = id.convert<int32_t>();
|
const auto as_i32 = id.convert<int32_t>();
|
||||||
@@ -258,7 +258,7 @@ TEST_CASE("identifier convert() rejects non-integer source types", "[utils][iden
|
|||||||
REQUIRE(null_to_i64.is_error());
|
REQUIRE(null_to_i64.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier convert() works with unsigned integer sources", "[utils][identifier][convert]") {
|
TEST_CASE("Identifier: Identifier convert() works with unsigned integer sources", "[utils][identifier][convert]") {
|
||||||
identifier id{uint16_t{500}};
|
identifier id{uint16_t{500}};
|
||||||
|
|
||||||
const auto as_i32 = id.convert<int32_t>();
|
const auto as_i32 = id.convert<int32_t>();
|
||||||
@@ -269,7 +269,7 @@ TEST_CASE("identifier convert() works with unsigned integer sources", "[utils][i
|
|||||||
REQUIRE(as_u8.is_error());
|
REQUIRE(as_u8.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier as() and convert() behave consistently for same integer type", "[utils][identifier][as][convert]") {
|
TEST_CASE("Identifier: Identifier as() and convert() behave consistently", "[utils][identifier][as][convert]") {
|
||||||
identifier id{uint64_t{77}};
|
identifier id{uint64_t{77}};
|
||||||
|
|
||||||
const auto as_u64 = id.as<uint64_t>();
|
const auto as_u64 = id.as<uint64_t>();
|
||||||
@@ -281,7 +281,7 @@ TEST_CASE("identifier as() and convert() behave consistently for same integer ty
|
|||||||
REQUIRE(*conv_u64 == 77);
|
REQUIRE(*conv_u64 == 77);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assign from value", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assign from value", "[utils][identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
value v1{int32_t{17}};
|
value v1{int32_t{17}};
|
||||||
@@ -301,7 +301,7 @@ TEST_CASE("identifier assign from value", "[utils][identifier][assign]") {
|
|||||||
REQUIRE(r3.is_error());
|
REQUIRE(r3.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier assign from value with exact type mapping", "[utils][identifier][assign]") {
|
TEST_CASE("Identifier: Identifier assign from value with exact type mapping", "[utils][identifier][assign]") {
|
||||||
identifier id;
|
identifier id;
|
||||||
|
|
||||||
SECTION("integer types") {
|
SECTION("integer types") {
|
||||||
@@ -328,7 +328,7 @@ TEST_CASE("identifier assign from value with exact type mapping", "[utils][ident
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("identifier::to_database_type converts internal value to database_type", "[identifier]") {
|
TEST_CASE("Identifier: Identifier::to_database_type converts internal value", "[identifier][to_database_type]") {
|
||||||
SECTION("null identifier becomes nullptr") {
|
SECTION("null identifier becomes nullptr") {
|
||||||
identifier id{nullptr};
|
identifier id{nullptr};
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ TEST_CASE("identifier::to_database_type converts internal value to database_type
|
|||||||
REQUIRE(std::holds_alternative<std::nullptr_t>(db_value));
|
REQUIRE(std::holds_alternative<std::nullptr_t>(db_value));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("signed integral identifier becomes same signed database type") {
|
SECTION("Identifier: Signed integral identifier becomes same signed database type") {
|
||||||
identifier id{int32_t{42}};
|
identifier id{int32_t{42}};
|
||||||
|
|
||||||
const auto db_value = id.to_database_type();
|
const auto db_value = id.to_database_type();
|
||||||
@@ -346,7 +346,7 @@ TEST_CASE("identifier::to_database_type converts internal value to database_type
|
|||||||
REQUIRE(std::get<int32_t>(db_value) == 42);
|
REQUIRE(std::get<int32_t>(db_value) == 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("unsigned integral identifier becomes same unsigned database type") {
|
SECTION("Identifier: Unsigned integral identifier becomes same unsigned database type") {
|
||||||
identifier id{uint64_t{123456789ULL}};
|
identifier id{uint64_t{123456789ULL}};
|
||||||
|
|
||||||
const auto db_value = id.to_database_type();
|
const auto db_value = id.to_database_type();
|
||||||
@@ -355,7 +355,7 @@ TEST_CASE("identifier::to_database_type converts internal value to database_type
|
|||||||
REQUIRE(std::get<uint64_t>(db_value) == 123456789ULL);
|
REQUIRE(std::get<uint64_t>(db_value) == 123456789ULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("string identifier becomes std::string database type") {
|
SECTION("Identifier: String identifier becomes std::string database type") {
|
||||||
identifier id{std::string{"customer_1"}};
|
identifier id{std::string{"customer_1"}};
|
||||||
|
|
||||||
const auto db_value = id.to_database_type();
|
const auto db_value = id.to_database_type();
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public:
|
|||||||
|
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
TEST_CASE("Basic publish/subscribe works", "[MessageBus]") {
|
TEST_CASE("MessageBus: Basic publish/subscribe works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ TEST_CASE("Basic publish/subscribe works", "[MessageBus]") {
|
|||||||
REQUIRE(counter == 3);
|
REQUIRE(counter == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Filtering works", "[MessageBus]") {
|
TEST_CASE("MessageBus: Filtering works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ TEST_CASE("Filtering works", "[MessageBus]") {
|
|||||||
REQUIRE(counter == 6); // only 2 + 4
|
REQUIRE(counter == 6); // only 2 + 4
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Member function subscription works", "[MessageBus]") {
|
TEST_CASE("MessageBus: Member function subscription works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
Receiver r;
|
Receiver r;
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ TEST_CASE("Member function subscription works", "[MessageBus]") {
|
|||||||
REQUIRE(r.received[1] == "world");
|
REQUIRE(r.received[1] == "world");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Shared_ptr instance subscription works", "[MessageBus]") {
|
TEST_CASE("MessageBus: SharedPtr instance subscription works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
const auto r = std::make_shared<Receiver>();
|
const auto r = std::make_shared<Receiver>();
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ TEST_CASE("Shared_ptr instance subscription works", "[MessageBus]") {
|
|||||||
REQUIRE(r->received[0] == "foo");
|
REQUIRE(r->received[0] == "foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("RAII unsubscription works", "[MessageBus]") {
|
TEST_CASE("MessageBus: RAII unsubscription works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ TEST_CASE("RAII unsubscription works", "[MessageBus]") {
|
|||||||
REQUIRE(counter == 5);
|
REQUIRE(counter == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Type-erased AnyMessage publish works", "[MessageBus]") {
|
TEST_CASE("MessageBus: Type-erased AnyMessage publish works", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ TEST_CASE("Type-erased AnyMessage publish works", "[MessageBus]") {
|
|||||||
REQUIRE(counter == 10);
|
REQUIRE(counter == 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Multiple subscribers all receive messages", "[MessageBus]") {
|
TEST_CASE("MessageBus: Multiple subscribers all receive messages", "[MessageBus]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
int a = 0, b = 0;
|
int a = 0, b = 0;
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ TEST_CASE("Multiple subscribers all receive messages", "[MessageBus]") {
|
|||||||
REQUIRE(b == 3);
|
REQUIRE(b == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Stress test with multi-threaded publish and subscribe", "[MessageBus][stress]") {
|
TEST_CASE("MessageBus: Stress test with multi-threaded publish and subscribe", "[MessageBus][stress]") {
|
||||||
message_bus bus;
|
message_bus bus;
|
||||||
std::atomic<int> counter{0};
|
std::atomic<int> counter{0};
|
||||||
constexpr int numThreads = 8;
|
constexpr int numThreads = 8;
|
||||||
|
|||||||
@@ -5,13 +5,15 @@
|
|||||||
#include "matador/utils/result.hpp"
|
#include "matador/utils/result.hpp"
|
||||||
|
|
||||||
namespace matador::test {
|
namespace matador::test {
|
||||||
|
namespace {
|
||||||
enum class math_error : int32_t {
|
enum class math_error : int32_t {
|
||||||
OK = 0,
|
OK = 0,
|
||||||
DIVISION_BY_ZERO = 1,
|
DIVISION_BY_ZERO = 1,
|
||||||
FAILURE = 2
|
FAILURE = 2
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
utils::result<float, math_error>divide(const float x, const float y) {
|
static utils::result<float, math_error>divide(const float x, const float y) {
|
||||||
if (y == 0) {
|
if (y == 0) {
|
||||||
return utils::failure(math_error::DIVISION_BY_ZERO);
|
return utils::failure(math_error::DIVISION_BY_ZERO);
|
||||||
}
|
}
|
||||||
@@ -19,15 +21,15 @@ utils::result<float, math_error>divide(const float x, const float y) {
|
|||||||
return utils::ok(x / y);
|
return utils::ok(x / y);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<float, math_error>multiply(const float x, const float y) {
|
static utils::result<float, math_error>multiply(const float x, const float y) {
|
||||||
return utils::ok(x * y);
|
return utils::ok(x * y);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<float, math_error>plus(const float x, const float y) {
|
static utils::result<float, math_error>plus(const float x, const float y) {
|
||||||
return utils::ok(x + y);
|
return utils::ok(x + y);
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::result<void, math_error>action_on_greater_42(const float i) {
|
static utils::result<void, math_error>action_on_greater_42(const float i) {
|
||||||
if (i > 42) {
|
if (i > 42) {
|
||||||
return utils::ok<void>();
|
return utils::ok<void>();
|
||||||
}
|
}
|
||||||
@@ -88,7 +90,7 @@ TEST_CASE("Test result", "[result]") {
|
|||||||
REQUIRE(!res2.is_ok());
|
REQUIRE(!res2.is_ok());
|
||||||
REQUIRE(res2.is_error());
|
REQUIRE(res2.is_error());
|
||||||
const auto e = res2.err();
|
const auto e = res2.err();
|
||||||
// REQUIRE(res2.err() == "division by zero error");
|
REQUIRE(res2.err() == "division by zero error");
|
||||||
|
|
||||||
res = test::divide(4, 2)
|
res = test::divide(4, 2)
|
||||||
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
.and_then([](const auto &val) { return test::multiply(val, 5); })
|
||||||
|
|||||||
@@ -4,21 +4,21 @@
|
|||||||
|
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
TEST_CASE("thread_pool creates and rejects zero-sized construction") {
|
TEST_CASE("ThreadPool: thread_pool creates and rejects zero-sized construction") {
|
||||||
REQUIRE_THROWS(thread_pool(0));
|
REQUIRE_THROWS(thread_pool(0));
|
||||||
|
|
||||||
const thread_pool tp(2);
|
const thread_pool tp(2);
|
||||||
REQUIRE(tp.size() == 2);
|
REQUIRE(tp.size() == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("thread_pool schedules and runs simple tasks") {
|
TEST_CASE("ThreadPool: thread_pool schedules and runs simple tasks") {
|
||||||
thread_pool pool(3);
|
thread_pool pool(3);
|
||||||
auto fut = pool.schedule([](cancel_token&, const int x) { return x * 10; }, 7);
|
auto fut = pool.schedule([](cancel_token&, const int x) { return x * 10; }, 7);
|
||||||
REQUIRE(fut);
|
REQUIRE(fut);
|
||||||
REQUIRE(fut.value().get() == 70);
|
REQUIRE(fut.value().get() == 70);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("thread_pool parallel computation and results") {
|
TEST_CASE("ThreadPool: thread_pool parallel computation and results") {
|
||||||
thread_pool pool(4);
|
thread_pool pool(4);
|
||||||
std::vector<std::future<int>> futs;
|
std::vector<std::future<int>> futs;
|
||||||
for (int i = 1; i <= 20; ++i) {
|
for (int i = 1; i <= 20; ++i) {
|
||||||
@@ -33,7 +33,7 @@ TEST_CASE("thread_pool parallel computation and results") {
|
|||||||
REQUIRE(sum == 230); // (2+3+...+21) = (21*22/2)-(1) = 231-1
|
REQUIRE(sum == 230); // (2+3+...+21) = (21*22/2)-(1) = 231-1
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("thread_pool rejects scheduling after shutdown") {
|
TEST_CASE("ThreadPool: thread_pool rejects scheduling after shutdown") {
|
||||||
thread_pool pool(2);
|
thread_pool pool(2);
|
||||||
pool.shutdown();
|
pool.shutdown();
|
||||||
auto result = pool.schedule([](cancel_token&) { return 1; });
|
auto result = pool.schedule([](cancel_token&) { return 1; });
|
||||||
@@ -41,7 +41,7 @@ TEST_CASE("thread_pool rejects scheduling after shutdown") {
|
|||||||
REQUIRE(result.err().find("shut down") != std::string::npos);
|
REQUIRE(result.err().find("shut down") != std::string::npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("thread_pool supports cancel_token for not-yet-run task") {
|
TEST_CASE("ThreadPool: thread_pool supports cancel_token for not-yet-run task") {
|
||||||
thread_pool pool(1);
|
thread_pool pool(1);
|
||||||
std::atomic ran{false};
|
std::atomic ran{false};
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ TEST_CASE("thread_pool supports cancel_token for not-yet-run task") {
|
|||||||
// If started before cancel, may be 100 (rare but possible)
|
// If started before cancel, may be 100 (rare but possible)
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("thread_pool: shutdown finishes all running tasks") {
|
TEST_CASE("ThreadPool: thread_pool: shutdown finishes all running tasks") {
|
||||||
thread_pool pool(2);
|
thread_pool pool(2);
|
||||||
std::atomic counter{0};
|
std::atomic counter{0};
|
||||||
std::vector<std::future<void>> futs;
|
std::vector<std::future<void>> futs;
|
||||||
|
|||||||
@@ -4,7 +4,26 @@
|
|||||||
|
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
TEST_CASE("Test version interface", "[version][interface]") {
|
TEST_CASE("Version: Test form string", "[version][from_string]") {
|
||||||
|
REQUIRE(version::from_string("1.2.3").is_ok());
|
||||||
|
REQUIRE(version::from_string("0.0.0").is_ok());
|
||||||
|
|
||||||
|
REQUIRE(version::from_string("1.2").is_error());
|
||||||
|
REQUIRE(version::from_string("1.2.3.4").is_error());
|
||||||
|
REQUIRE(version::from_string("1.2.3abc").is_error());
|
||||||
|
REQUIRE(version::from_string("abc").is_error());
|
||||||
|
REQUIRE(version::from_string("-1.2.3").is_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Version: Test comparison", "[version][comparison]") {
|
||||||
|
REQUIRE(version(1, 2, 3) == version(1, 2, 3));
|
||||||
|
REQUIRE(version(1, 2, 3) < version(1, 2, 4));
|
||||||
|
REQUIRE(version(1, 2, 3) < version(1, 3, 0));
|
||||||
|
REQUIRE(version(1, 2, 3) < version(2, 0, 0));
|
||||||
|
REQUIRE(version(2, 0, 0) > version(1, 9, 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Version: Test interface", "[version][interface]") {
|
||||||
version v0;
|
version v0;
|
||||||
|
|
||||||
REQUIRE(v0.major() == 0);
|
REQUIRE(v0.major() == 0);
|
||||||
@@ -38,8 +57,8 @@ TEST_CASE("Test version interface", "[version][interface]") {
|
|||||||
REQUIRE(v2 == v1);
|
REQUIRE(v2 == v1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test version parsing", "[version][parse]") {
|
TEST_CASE("Version: Test parsing", "[version][parse]") {
|
||||||
const auto version_str{"13.67.34"};
|
constexpr auto version_str{"13.67.34"};
|
||||||
const auto v1 = version::from_string(version_str);
|
const auto v1 = version::from_string(version_str);
|
||||||
REQUIRE(v1.is_ok());
|
REQUIRE(v1.is_ok());
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,64 @@ struct recipe {
|
|||||||
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<const utils::primary_key_attribute &PkAttribute>
|
||||||
|
struct recipe_pk_generator;
|
||||||
|
|
||||||
|
template<const utils::primary_key_attribute &PkAttribute>
|
||||||
|
struct ingredient_pk_generator {
|
||||||
|
unsigned int id{};
|
||||||
|
std::string name;
|
||||||
|
object::collection<object::object_ptr<recipe_pk_generator<PkAttribute>>> recipes{};
|
||||||
|
|
||||||
|
ingredient_pk_generator() = default;
|
||||||
|
|
||||||
|
explicit ingredient_pk_generator(std::string name)
|
||||||
|
: name(std::move(name)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ingredient_pk_generator(std::string name, std::vector<object::object_ptr<recipe_pk_generator<PkAttribute>>> recps)
|
||||||
|
: name(std::move(name))
|
||||||
|
, recipes(std::move(recps)){}
|
||||||
|
|
||||||
|
template<class Operator>
|
||||||
|
void process(Operator &op) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::primary_key(op, "id", id, PkAttribute);
|
||||||
|
field::attribute(op, "name", name, UniqueVarChar255);
|
||||||
|
field::has_many_to_many(op, "recipe_ingredients", recipes, "ingredient_id", "recipe_id", utils::CascadeAllFetchEager);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<const utils::primary_key_attribute &PkAttribute>
|
||||||
|
struct recipe_pk_generator {
|
||||||
|
unsigned int id{};
|
||||||
|
std::string name;
|
||||||
|
object::collection<object::object_ptr<ingredient_pk_generator<PkAttribute>>> ingredients{};
|
||||||
|
|
||||||
|
recipe_pk_generator() = default;
|
||||||
|
explicit recipe_pk_generator(std::string name)
|
||||||
|
: name(std::move(name)) {
|
||||||
|
}
|
||||||
|
recipe_pk_generator(std::string name, std::vector<object::object_ptr<ingredient_pk_generator<PkAttribute>>> ings)
|
||||||
|
: name(std::move(name))
|
||||||
|
, ingredients(std::move(ings)){}
|
||||||
|
|
||||||
|
template<class Operator>
|
||||||
|
void process(Operator &op) {
|
||||||
|
namespace field = matador::access;
|
||||||
|
field::primary_key(op, "id", id, PkAttribute);
|
||||||
|
field::attribute(op, "name", name, UniqueVarChar255);
|
||||||
|
field::has_many_to_many(op, "recipe_ingredients", ingredients, utils::CascadeAllFetchLazy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using ingredient_identity = ingredient_pk_generator<utils::Identity>;
|
||||||
|
using ingredient_table = ingredient_pk_generator<utils::Table>;
|
||||||
|
using ingredient_sequence = ingredient_pk_generator<utils::Sequence>;
|
||||||
|
using recipe_identity = recipe_pk_generator<utils::Identity>;
|
||||||
|
using recipe_table = recipe_pk_generator<utils::Table>;
|
||||||
|
using recipe_sequence = recipe_pk_generator<utils::Sequence>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //QUERY_RECIPE_HPP
|
#endif //QUERY_RECIPE_HPP
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ CPMAddPackage("gh:catchorg/Catch2@3.14.0")
|
|||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
|
||||||
|
|
||||||
add_executable(OrmTests
|
add_executable(OrmTests
|
||||||
|
../utils/RecordingObserver.cpp
|
||||||
|
../utils/RecordingObserver.hpp
|
||||||
backend/test_backend_service.cpp
|
backend/test_backend_service.cpp
|
||||||
backend/test_backend_service.hpp
|
backend/test_backend_service.hpp
|
||||||
backend/test_connection.cpp
|
backend/test_connection.cpp
|
||||||
@@ -16,6 +18,7 @@ add_executable(OrmTests
|
|||||||
query/ColumnExpressionTest.cpp
|
query/ColumnExpressionTest.cpp
|
||||||
query/ColumnGeneratorTest.cpp
|
query/ColumnGeneratorTest.cpp
|
||||||
query/CriteriaTests.cpp
|
query/CriteriaTests.cpp
|
||||||
|
query/DeleteQueryBuilderTest.cpp
|
||||||
query/GeneratorTests.cpp
|
query/GeneratorTests.cpp
|
||||||
query/InsertQueryBuilderTest.cpp
|
query/InsertQueryBuilderTest.cpp
|
||||||
query/QueryBuilderTest.cpp
|
query/QueryBuilderTest.cpp
|
||||||
@@ -32,8 +35,6 @@ add_executable(OrmTests
|
|||||||
sql/StatementCacheTest.cpp
|
sql/StatementCacheTest.cpp
|
||||||
utils/auto_reset_event.cpp
|
utils/auto_reset_event.cpp
|
||||||
utils/auto_reset_event.hpp
|
utils/auto_reset_event.hpp
|
||||||
../utils/RecordingObserver.hpp
|
|
||||||
../utils/RecordingObserver.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)
|
target_link_libraries(OrmTests matador-orm matador-core Catch2::Catch2WithMain)
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
#include <catch2/catch_test_macros.hpp>
|
||||||
|
|
||||||
|
#include "matador/object/object_ptr.hpp"
|
||||||
|
|
||||||
|
#include "matador/sql/backend_provider.hpp"
|
||||||
|
#include "matador/sql/connection.hpp"
|
||||||
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/schema.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/delete_query_builder.hpp"
|
||||||
|
|
||||||
|
#include "QueryFixture.hpp"
|
||||||
|
|
||||||
|
#include "../../models/author.hpp"
|
||||||
|
#include "../../models/book.hpp"
|
||||||
|
#include "../../models/airplane.hpp"
|
||||||
|
#include "../../models/flight.hpp"
|
||||||
|
#include "../../models/recipe.hpp"
|
||||||
|
|
||||||
|
using namespace matador::object;
|
||||||
|
using namespace matador::sql;
|
||||||
|
using namespace matador::query;
|
||||||
|
using namespace matador::utils;
|
||||||
|
using namespace matador::test;
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(QueryFixture, "delete query builder test", "[query][delete_query_builder]") {
|
||||||
|
schema scm;
|
||||||
|
auto result = scm.attach<airplane>("airplanes")
|
||||||
|
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
|
||||||
|
REQUIRE(result);
|
||||||
|
|
||||||
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
|
delete_query_builder<airplane> dqb(scm, contexts_by_type);
|
||||||
|
|
||||||
|
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
|
||||||
|
auto build_result = dqb.build(a380);
|
||||||
|
REQUIRE(build_result.is_ok());
|
||||||
|
|
||||||
|
const auto& stmts = *build_result;
|
||||||
|
REQUIRE(stmts.size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(QueryFixture, "Test delete builder has many", "[query][delete_query_builder][has_many]") {
|
||||||
|
schema scm;
|
||||||
|
const auto result = scm.attach<book>("books")
|
||||||
|
.and_then( [&scm] { return scm.attach<author>("authors"); } );
|
||||||
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
|
auto s_king = make_object<author>(1, "Steven", "King", "21.9.1947", 1956, false);
|
||||||
|
|
||||||
|
s_king->books.push_back(make_object<book>(2, "Carrie", object_ptr<author>{}, 1974));
|
||||||
|
s_king->books.push_back(make_object<book>(3, "The Shining", object_ptr<author>{}, 1977));
|
||||||
|
s_king->books.push_back(make_object<book>(4, "It", object_ptr<author>{}, 1986));
|
||||||
|
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
|
||||||
|
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
|
||||||
|
|
||||||
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
|
delete_query_builder<author> dqb(scm, contexts_by_type);
|
||||||
|
|
||||||
|
auto build_result = dqb.build(s_king);
|
||||||
|
REQUIRE(build_result.is_ok());
|
||||||
|
|
||||||
|
const auto& stmts = *build_result;
|
||||||
|
REQUIRE_FALSE(stmts.empty());
|
||||||
|
REQUIRE(stmts.size() == 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(QueryFixture, "Test delete builder has many to many", "[query][delete_query_builder][many_to_many]") {
|
||||||
|
schema scm;
|
||||||
|
const auto result = scm.attach<recipe>("recipes")
|
||||||
|
.and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } );
|
||||||
|
REQUIRE(result.is_ok());
|
||||||
|
|
||||||
|
std::vector ingredients {
|
||||||
|
make_object<ingredient>(1, "Apple"),
|
||||||
|
make_object<ingredient>(2, "Strawberry"),
|
||||||
|
make_object<ingredient>(3, "Pineapple"),
|
||||||
|
make_object<ingredient>(4, "Sugar"),
|
||||||
|
make_object<ingredient>(5, "Flour"),
|
||||||
|
make_object<ingredient>(6, "Butter"),
|
||||||
|
make_object<ingredient>(7, "Beans")
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &i : ingredients) {
|
||||||
|
i.change_state(object_state::Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector recipes {
|
||||||
|
make_object<recipe>(1, "Apple Pie", std::vector{ingredients[0], ingredients[3], ingredients[4]}),
|
||||||
|
make_object<recipe>(2, "Strawberry Cake", std::vector{ingredients[5], ingredients[6]}),
|
||||||
|
make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
|
||||||
|
};
|
||||||
|
|
||||||
|
for (auto &r : recipes) {
|
||||||
|
r.change_state(object_state::Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
|
delete_query_builder<recipe> dqb(scm, contexts_by_type);
|
||||||
|
|
||||||
|
auto build_result = dqb.build(recipes[0]);
|
||||||
|
REQUIRE(build_result.is_ok());
|
||||||
|
|
||||||
|
const auto& stmts = *build_result;
|
||||||
|
REQUIRE_FALSE(stmts.empty());
|
||||||
|
REQUIRE(stmts.size() == 7);
|
||||||
|
}
|
||||||
@@ -6,13 +6,11 @@
|
|||||||
#include "matador/sql/connection.hpp"
|
#include "matador/sql/connection.hpp"
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
#include "matador/query/query_contexts.hpp"
|
|
||||||
#include "matador/query/query.hpp"
|
|
||||||
#include "matador/query/schema.hpp"
|
#include "matador/query/schema.hpp"
|
||||||
|
|
||||||
#include "matador/query/insert_query_builder.hpp"
|
#include "matador/query/insert_query_builder.hpp"
|
||||||
|
|
||||||
#include "../backend/test_backend_service.hpp"
|
#include "QueryFixture.hpp"
|
||||||
|
|
||||||
#include "../../models/author.hpp"
|
#include "../../models/author.hpp"
|
||||||
#include "../../models/book.hpp"
|
#include "../../models/book.hpp"
|
||||||
@@ -24,58 +22,15 @@ using namespace matador::object;
|
|||||||
using namespace matador::sql;
|
using namespace matador::sql;
|
||||||
using namespace matador::query;
|
using namespace matador::query;
|
||||||
using namespace matador::utils;
|
using namespace matador::utils;
|
||||||
|
|
||||||
std::unordered_map<std::type_index, query_contexts> to_contexts_by_name(const schema& scm, const dialect& d) {
|
|
||||||
std::unordered_map<std::type_index, query_contexts> contexts_by_type;
|
|
||||||
for (const auto &[type, node] : scm) {
|
|
||||||
query_contexts queries;
|
|
||||||
|
|
||||||
// SELECT all
|
|
||||||
queries.select_all = select(node.table())
|
|
||||||
.from(node.name())
|
|
||||||
.compile(d);
|
|
||||||
if (node.table().has_primary_key()) {
|
|
||||||
// SELECT one
|
|
||||||
queries.select_one = select(node.table())
|
|
||||||
.from(node.name())
|
|
||||||
.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(d);
|
|
||||||
// UPDATE one
|
|
||||||
auto update_set = update(node.table());
|
|
||||||
for (const auto &col: node.table().columns()) {
|
|
||||||
update_set.set(col, _);
|
|
||||||
}
|
|
||||||
queries.update_one = update_set.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(d);
|
|
||||||
// DELETE one
|
|
||||||
queries.delete_one = remove()
|
|
||||||
.from(node.name())
|
|
||||||
.where(*node.table().primary_key_column() == _)
|
|
||||||
.compile(d);
|
|
||||||
}
|
|
||||||
// INSERT one
|
|
||||||
queries.insert = insert()
|
|
||||||
.into(node.name(), node.table())
|
|
||||||
.values(generator::placeholders(node.table().columns().size()))
|
|
||||||
.compile(d);
|
|
||||||
|
|
||||||
contexts_by_type[node.node().type_index()] = queries;
|
|
||||||
}
|
|
||||||
|
|
||||||
return contexts_by_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
|
||||||
using namespace matador::test;
|
using namespace matador::test;
|
||||||
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
|
||||||
connection db("noop://noop.db");
|
|
||||||
|
|
||||||
|
TEST_CASE_METHOD(QueryFixture, "insert query builder test", "[query][insert_query_builder]") {
|
||||||
schema scm;
|
schema scm;
|
||||||
auto result = scm.attach<airplane>("airplanes")
|
auto result = scm.attach<airplane>("airplanes")
|
||||||
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
|
.and_then( [&scm] { return scm.attach<flight>("flights"); } );
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
|
|
||||||
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect());
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
insert_query_builder<airplane> iqb(scm, contexts_by_type);
|
insert_query_builder<airplane> iqb(scm, contexts_by_type);
|
||||||
|
|
||||||
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
|
const auto a380 = make_object<airplane>(1, "Boeing", "A380" );
|
||||||
@@ -86,11 +41,7 @@ TEST_CASE("insert query builder test", "[query][insert_query_builder]") {
|
|||||||
REQUIRE(stmts.size() == 1);
|
REQUIRE(stmts.size() == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_many]") {
|
TEST_CASE_METHOD(QueryFixture, "Test insert builder has many", "[query][insert_query_builder][has_many]") {
|
||||||
using namespace matador::test;
|
|
||||||
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
|
||||||
connection db("noop://noop.db");
|
|
||||||
|
|
||||||
schema scm;
|
schema scm;
|
||||||
const auto result = scm.attach<book>("books")
|
const auto result = scm.attach<book>("books")
|
||||||
.and_then( [&scm] { return scm.attach<author>("authors"); } );
|
.and_then( [&scm] { return scm.attach<author>("authors"); } );
|
||||||
@@ -104,7 +55,7 @@ TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_man
|
|||||||
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
|
s_king->books.push_back(make_object<book>(5, "Misery", object_ptr<author>{}, 1987));
|
||||||
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
|
s_king->books.push_back(make_object<book>(6, "The Dark Tower: The Gunslinger", object_ptr<author>{}, 1982));
|
||||||
|
|
||||||
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect());
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
insert_query_builder<author> iqb(scm, contexts_by_type);
|
insert_query_builder<author> iqb(scm, contexts_by_type);
|
||||||
|
|
||||||
auto build_result = iqb.build(s_king);
|
auto build_result = iqb.build(s_king);
|
||||||
@@ -115,11 +66,7 @@ TEST_CASE("Test insert builder has many", "[query][insert_query_builder][has_man
|
|||||||
REQUIRE(stmts.size() == 6);
|
REQUIRE(stmts.size() == 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Test insert builder has many to many", "[query][insert_query_builder][many_to_many]") {
|
TEST_CASE_METHOD(QueryFixture, "Test insert builder has many to many", "[query][insert_query_builder][many_to_many]") {
|
||||||
using namespace matador::test;
|
|
||||||
backend_provider::instance().register_backend("noop", std::make_unique<orm::test_backend_service>());
|
|
||||||
connection db("noop://noop.db");
|
|
||||||
|
|
||||||
schema scm;
|
schema scm;
|
||||||
const auto result = scm.attach<recipe>("recipes")
|
const auto result = scm.attach<recipe>("recipes")
|
||||||
.and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } );
|
.and_then( [&scm] { return scm.attach<ingredient>("ingredients"); } );
|
||||||
@@ -141,7 +88,7 @@ TEST_CASE("Test insert builder has many to many", "[query][insert_query_builder]
|
|||||||
make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
|
make_object<recipe>(3, "Pineapple Pie", std::vector{ingredients[0], ingredients[1], ingredients[2]})
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto contexts_by_type = to_contexts_by_name(scm, db.dialect());
|
const auto contexts_by_type = to_contexts_by_name(scm, db->dialect());
|
||||||
insert_query_builder<recipe> iqb(scm, contexts_by_type);
|
insert_query_builder<recipe> iqb(scm, contexts_by_type);
|
||||||
|
|
||||||
auto build_result = iqb.build(recipes[0]);
|
auto build_result = iqb.build(recipes[0]);
|
||||||
|
|||||||
@@ -24,13 +24,13 @@ TEST_CASE_METHOD(QueryFixture, "Test alter table sql statement", "[query][alter]
|
|||||||
.add_constraint("FK_employees_dep_id")
|
.add_constraint("FK_employees_dep_id")
|
||||||
.foreign_key("dep_id"_col)
|
.foreign_key("dep_id"_col)
|
||||||
.references("departments", {"id"_col})
|
.references("departments", {"id"_col})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(ALTER TABLE "employees" ADD CONSTRAINT FK_employees_dep_id FOREIGN KEY ("dep_id") REFERENCES departments ("id"))");
|
REQUIRE(result == R"(ALTER TABLE "employees" ADD CONSTRAINT FK_employees_dep_id FOREIGN KEY ("dep_id") REFERENCES departments ("id"))");
|
||||||
result = alter()
|
result = alter()
|
||||||
.table("employees")
|
.table("employees")
|
||||||
.drop_constraint("FK_employees_dep_id")
|
.drop_constraint("FK_employees_dep_id")
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(ALTER TABLE "employees" DROP CONSTRAINT FK_employees_dep_id)");
|
REQUIRE(result == R"(ALTER TABLE "employees" DROP CONSTRAINT FK_employees_dep_id)");
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query
|
|||||||
.constraints({
|
.constraints({
|
||||||
constraint("PK_person").primary_key({"id"})
|
constraint("PK_person").primary_key({"id"})
|
||||||
})
|
})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query
|
|||||||
column("name", basic_type::Varchar, 255),
|
column("name", basic_type::Varchar, 255),
|
||||||
column("age", basic_type::UInt16)
|
column("age", basic_type::UInt16)
|
||||||
})
|
})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL AUTO INCREMENT PRIMARY KEY, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL))##");
|
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL AUTO INCREMENT PRIMARY KEY, "name" VARCHAR(255) NOT NULL, "age" INTEGER NOT NULL))##");
|
||||||
|
|
||||||
@@ -81,7 +81,7 @@ TEST_CASE_METHOD(QueryFixture, "Test create table sql statement string", "[query
|
|||||||
TEST_CASE_METHOD(QueryFixture, "Test drop table sql statement string", "[query]") {
|
TEST_CASE_METHOD(QueryFixture, "Test drop table sql statement string", "[query]") {
|
||||||
const auto result = drop()
|
const auto result = drop()
|
||||||
.table("person")
|
.table("person")
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(DROP TABLE "person")");
|
REQUIRE(result == R"(DROP TABLE "person")");
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ TEST_CASE_METHOD(QueryFixture, "Test drop table sql statement string", "[query]"
|
|||||||
TEST_CASE_METHOD(QueryFixture, "Test select all columns with asterisk", "[query][select][asterisk]") {
|
TEST_CASE_METHOD(QueryFixture, "Test select all columns with asterisk", "[query][select][asterisk]") {
|
||||||
const auto result = select()
|
const auto result = select()
|
||||||
.from("person")
|
.from("person")
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT * FROM "person")");
|
REQUIRE(result == R"(SELECT * FROM "person")");
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select all columns with asterisk", "[query]
|
|||||||
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string", "[query]") {
|
TEST_CASE_METHOD(QueryFixture, "Test select sql statement string", "[query]") {
|
||||||
const auto result = select({"id", "name", "age"})
|
const auto result = select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person")");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person")");
|
||||||
}
|
}
|
||||||
@@ -108,7 +108,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert sql statement string", "[query]") {
|
|||||||
"id", "name", "age"
|
"id", "name", "age"
|
||||||
})
|
})
|
||||||
.values({7U, "george", 65U})
|
.values({7U, "george", 65U})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
|
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (7, 'george', 65))");
|
||||||
}
|
}
|
||||||
@@ -118,7 +118,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") {
|
|||||||
.set("id", 7U)
|
.set("id", 7U)
|
||||||
.set("name", "george")
|
.set("name", "george")
|
||||||
.set("age", 65U)
|
.set("age", 65U)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
|
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65)");
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update sql statement string", "[query]") {
|
|||||||
.order_by("id").asc()
|
.order_by("id").asc()
|
||||||
.limit(3)
|
.limit(3)
|
||||||
.offset(2)
|
.offset(2)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "id" > 9 ORDER BY "id" ASC LIMIT 3 OFFSET 2)");
|
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "id" > 9 ORDER BY "id" ASC LIMIT 3 OFFSET 2)");
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ TEST_CASE_METHOD(QueryFixture, "Test update returning statement", "[query][updat
|
|||||||
.set("age", 65U)
|
.set("age", 65U)
|
||||||
.where("name"_col == "george")
|
.where("name"_col == "george")
|
||||||
.returning("id"_col, "name"_col, "age"_col)
|
.returning("id"_col, "name"_col, "age"_col)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' RETURNING "id", "name", "age")");
|
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' RETURNING "id", "name", "age")");
|
||||||
}
|
}
|
||||||
@@ -155,13 +155,13 @@ TEST_CASE_METHOD(QueryFixture, "Test update limit sql statement", "[query][updat
|
|||||||
.where("name"_col == "george")
|
.where("name"_col == "george")
|
||||||
.order_by("id"_col).asc()
|
.order_by("id"_col).asc()
|
||||||
.limit(2)
|
.limit(2)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
REQUIRE(result == R"(UPDATE "person" SET "id"=7, "name"='george', "age"=65 WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE_METHOD(QueryFixture, "Test delete sql statement string", "[query]") {
|
TEST_CASE_METHOD(QueryFixture, "Test delete sql statement string", "[query]") {
|
||||||
const auto result = remove().from("person").str(*db);
|
const auto result = remove().from("person").str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(DELETE FROM "person")");
|
REQUIRE(result == R"(DELETE FROM "person")");
|
||||||
}
|
}
|
||||||
@@ -172,7 +172,7 @@ TEST_CASE_METHOD(QueryFixture, "Test delete limit sql statement", "[query][delet
|
|||||||
.where("name"_col == "george")
|
.where("name"_col == "george")
|
||||||
.order_by("id"_col).asc()
|
.order_by("id"_col).asc()
|
||||||
.limit(2)
|
.limit(2)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
REQUIRE(result == R"(DELETE FROM "person" WHERE "name" = 'george' ORDER BY "id" ASC LIMIT 2)");
|
||||||
}
|
}
|
||||||
@@ -181,14 +181,14 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with where clau
|
|||||||
auto result = select({"id", "name", "age"})
|
auto result = select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.where("id"_col == 8 && "age"_col > 50)
|
.where("id"_col == 8 && "age"_col > 50)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = 8 AND "age" > 50))");
|
||||||
|
|
||||||
result = select({"id", "name", "age"})
|
result = select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.where("id"_col == _ && "age"_col > 50)
|
.where("id"_col == _ && "age"_col > 50)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" WHERE ("id" = ? AND "age" > 50))");
|
||||||
}
|
}
|
||||||
@@ -197,14 +197,14 @@ TEST_CASE_METHOD(QueryFixture, "Test insert sql statement with placeholder", "[q
|
|||||||
auto result = insert()
|
auto result = insert()
|
||||||
.into("person", {"id", "name", "age"})
|
.into("person", {"id", "name", "age"})
|
||||||
.values({_, _, _})
|
.values({_, _, _})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
|
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (?, ?, ?))");
|
||||||
|
|
||||||
result = insert()
|
result = insert()
|
||||||
.into("person", {"id", "name", "age"})
|
.into("person", {"id", "name", "age"})
|
||||||
.values({9, "george", _})
|
.values({9, "george", _})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?))");
|
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?))");
|
||||||
}
|
}
|
||||||
@@ -214,7 +214,7 @@ TEST_CASE_METHOD(QueryFixture, "Test insert sql statement with returning", "[que
|
|||||||
.into("person", {"id", "name", "age"})
|
.into("person", {"id", "name", "age"})
|
||||||
.values({9, "george", _})
|
.values({9, "george", _})
|
||||||
.returning("id"_col, "name"_col, "age"_col)
|
.returning("id"_col, "name"_col, "age"_col)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?) RETURNING "id", "name", "age")");
|
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "age") VALUES (9, 'george', ?) RETURNING "id", "name", "age")");
|
||||||
}
|
}
|
||||||
@@ -223,7 +223,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with order by",
|
|||||||
const auto result = select({"id", "name", "age"})
|
const auto result = select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.order_by("name"_col).asc()
|
.order_by("name"_col).asc()
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "name" ASC)");
|
||||||
}
|
}
|
||||||
@@ -232,7 +232,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with group by",
|
|||||||
const auto result = select({"id", "name", "age"})
|
const auto result = select({"id", "name", "age"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.group_by("age"_col)
|
.group_by("age"_col)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" GROUP BY "age")");
|
||||||
}
|
}
|
||||||
@@ -243,7 +243,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select sql statement string with offset and
|
|||||||
.order_by("id"_col).asc()
|
.order_by("id"_col).asc()
|
||||||
.limit(20)
|
.limit(20)
|
||||||
.offset(10)
|
.offset(10)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)");
|
REQUIRE(result == R"(SELECT "id", "name", "age" FROM "person" ORDER BY "id" ASC LIMIT 20 OFFSET 10)");
|
||||||
}
|
}
|
||||||
@@ -259,20 +259,20 @@ TEST_CASE_METHOD(QueryFixture, "Test create, insert and select a blob column", "
|
|||||||
.constraints({
|
.constraints({
|
||||||
constraint("PK_person").primary_key({"id"})
|
constraint("PK_person").primary_key({"id"})
|
||||||
})
|
})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
REQUIRE(result == R"##(CREATE TABLE "person" ("id" BIGINT NOT NULL, "name" VARCHAR(255) NOT NULL, "data" BLOB NOT NULL, CONSTRAINT PK_person PRIMARY KEY (id)))##");
|
||||||
|
|
||||||
result = insert()
|
result = insert()
|
||||||
.into("person", {"id", "name", "data"})
|
.into("person", {"id", "name", "data"})
|
||||||
.values({7U, "george", blob_type_t{1, 'A', 3, 4}})
|
.values({7U, "george", blob_type_t{1, 'A', 3, 4}})
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))");
|
REQUIRE(result == R"(INSERT INTO "person" ("id", "name", "data") VALUES (7, 'george', X'01410304'))");
|
||||||
|
|
||||||
result = select({"id", "name", "data"})
|
result = select({"id", "name", "data"})
|
||||||
.from("person")
|
.from("person")
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "id", "name", "data" FROM "person")");
|
REQUIRE(result == R"(SELECT "id", "name", "data" FROM "person")");
|
||||||
}
|
}
|
||||||
@@ -286,7 +286,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select statement with join_left", "[query][
|
|||||||
.from(table{"flight"}.as("f"))
|
.from(table{"flight"}.as("f"))
|
||||||
.join_left(table{"airplane"}.as("ap"))
|
.join_left(table{"airplane"}.as("ap"))
|
||||||
.on(col1 == col2)
|
.on(col1 == col2)
|
||||||
.str(*db);
|
.str(db->dialect());
|
||||||
|
|
||||||
REQUIRE(result == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" "f" LEFT JOIN "airplane" "ap" ON "f"."airplane_id" = "ap"."id")");
|
REQUIRE(result == R"(SELECT "f"."id", "ap"."brand", "f"."pilot_name" FROM "flight" "f" LEFT JOIN "airplane" "ap" ON "f"."airplane_id" = "ap"."id")");
|
||||||
}
|
}
|
||||||
@@ -301,7 +301,7 @@ TEST_CASE_METHOD(QueryFixture, "Test select statement with join_left", "[query][
|
|||||||
//
|
//
|
||||||
// const auto result = select<author>(scm)
|
// const auto result = select<author>(scm)
|
||||||
// .from("authors"_tab.as("T01"))
|
// .from("authors"_tab.as("T01"))
|
||||||
// .str(*db);
|
// .str(db->dialect());
|
||||||
//
|
//
|
||||||
// const auto expected_sql = R"(SELECT "T01"."id", "T01"."first_name", "T01"."last_name", "T01"."date_of_birth", "T01"."year_of_birth", "T01"."distinguished" FROM "authors" "T01")";
|
// const auto expected_sql = R"(SELECT "T01"."id", "T01"."first_name", "T01"."last_name", "T01"."date_of_birth", "T01"."year_of_birth", "T01"."distinguished" FROM "authors" "T01")";
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
#include "matador/sql/interface/connection_impl.hpp"
|
#include "matador/sql/interface/connection_impl.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/basic_schema.hpp"
|
||||||
|
#include "matador/query/schema_utils.hpp"
|
||||||
|
|
||||||
namespace matador::test {
|
namespace matador::test {
|
||||||
|
|
||||||
QueryFixture::QueryFixture() {
|
QueryFixture::QueryFixture() {
|
||||||
@@ -12,4 +15,12 @@ QueryFixture::QueryFixture() {
|
|||||||
db = std::make_unique<sql::connection>("noop://noop.db");
|
db = std::make_unique<sql::connection>("noop://noop.db");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::type_index, query::query_contexts> QueryFixture::to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) {
|
||||||
|
std::unordered_map<std::type_index, query::query_contexts> contexts_by_type;
|
||||||
|
for (const auto &[type, node] : scm) {
|
||||||
|
contexts_by_type[node.node().type_index()] = to_query_contexts(node, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
return contexts_by_type;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,8 +3,14 @@
|
|||||||
|
|
||||||
#include "matador/sql/connection.hpp"
|
#include "matador/sql/connection.hpp"
|
||||||
|
|
||||||
|
#include "matador/query/query_contexts.hpp"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
namespace matador::query {
|
||||||
|
class basic_schema;
|
||||||
|
}
|
||||||
|
|
||||||
namespace matador::test {
|
namespace matador::test {
|
||||||
|
|
||||||
class QueryFixture {
|
class QueryFixture {
|
||||||
@@ -12,6 +18,9 @@ public:
|
|||||||
QueryFixture();
|
QueryFixture();
|
||||||
~QueryFixture() = default;
|
~QueryFixture() = default;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static std::unordered_map<std::type_index, query::query_contexts> to_contexts_by_name(const query::basic_schema& scm, const sql::dialect& d) ;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::unique_ptr<sql::connection> db;
|
std::unique_ptr<sql::connection> db;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ TEST_CASE("Create sql query data for entity with eager has one", "[query][entity
|
|||||||
auto q = eqb.build<flight>(*col == _);
|
auto q = eqb.build<flight>(*col == _);
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
|
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t02"."id", "t02"."brand", "t02"."model", "t01"."pilot_name" FROM "flights" "t01" LEFT JOIN "airplanes" "t02" ON "t01"."airplane_id" = "t02"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t02"."id", "t02"."brand", "t02"."model", "t01"."pilot_name" FROM "flights" "t01" LEFT JOIN "airplanes" "t02" ON "t01"."airplane_id" = "t02"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
@@ -111,7 +111,7 @@ TEST_CASE("Create sql query data for entity with eager belongs to", "[query][ent
|
|||||||
auto q = eqb.build<book>(*col == _);
|
auto q = eqb.build<book>(*col == _);
|
||||||
|
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."title", "t02"."id", "t02"."first_name", "t02"."last_name", "t02"."date_of_birth", "t02"."year_of_birth", "t02"."distinguished", "t01"."published_in" FROM "books" "t01" LEFT JOIN "authors" "t02" ON "t01"."author_id" = "t02"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."title", "t02"."id", "t02"."first_name", "t02"."last_name", "t02"."date_of_birth", "t02"."year_of_birth", "t02"."distinguished", "t01"."published_in" FROM "books" "t01" LEFT JOIN "authors" "t02" ON "t01"."author_id" = "t02"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ TEST_CASE("Create sql query data for entity with eager has many belongs to", "[q
|
|||||||
auto q = eqb.build<order>(*col == _);
|
auto q = eqb.build<order>(*col == _);
|
||||||
|
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."order_id", "t01"."order_date", "t01"."required_date", "t01"."shipped_date", "t01"."ship_via", "t01"."freight", "t01"."ship_name", "t01"."ship_address", "t01"."ship_city", "t01"."ship_region", "t01"."ship_postal_code", "t01"."ship_country", "t02"."order_details_id", "t02"."order_id" FROM "orders" "t01" LEFT JOIN "order_details" "t02" ON "t01"."order_id" = "t02"."order_id" WHERE "t01"."order_id" = ? ORDER BY "t01"."order_id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."order_id", "t01"."order_date", "t01"."required_date", "t01"."shipped_date", "t01"."ship_via", "t01"."freight", "t01"."ship_name", "t01"."ship_address", "t01"."ship_city", "t01"."ship_region", "t01"."ship_postal_code", "t01"."ship_country", "t02"."order_details_id", "t02"."order_id" FROM "orders" "t01" LEFT JOIN "order_details" "t02" ON "t01"."order_id" = "t02"."order_id" WHERE "t01"."order_id" = ? ORDER BY "t01"."order_id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ TEST_CASE("Create sql query data for entity with eager many to many", "[query][e
|
|||||||
auto q = eqb.build<ingredient>(*col == _);
|
auto q = eqb.build<ingredient>(*col == _);
|
||||||
|
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t03"."id", "t03"."name" FROM "ingredients" "t01" LEFT JOIN "recipe_ingredients" "t02" ON "t01"."id" = "t02"."ingredient_id" LEFT JOIN "recipes" "t03" ON "t02"."recipe_id" = "t03"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t03"."id", "t03"."name" FROM "ingredients" "t01" LEFT JOIN "recipe_ingredients" "t02" ON "t01"."id" = "t02"."ingredient_id" LEFT JOIN "recipes" "t03" ON "t02"."recipe_id" = "t03"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
@@ -315,7 +315,7 @@ TEST_CASE("Create sql query data for entity with eager many to many (inverse par
|
|||||||
auto q = eqb.build<course>(*col == _);
|
auto q = eqb.build<course>(*col == _);
|
||||||
|
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."title", "t03"."id", "t03"."name" FROM "courses" "t01" LEFT JOIN "student_courses" "t02" ON "t01"."id" = "t02"."course_id" LEFT JOIN "students" "t03" ON "t02"."student_id" = "t03"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."title", "t03"."id", "t03"."name" FROM "courses" "t01" LEFT JOIN "student_courses" "t02" ON "t01"."id" = "t02"."course_id" LEFT JOIN "students" "t03" ON "t02"."student_id" = "t03"."id" WHERE "t01"."id" = ? ORDER BY "t01"."id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
@@ -369,11 +369,11 @@ TEST_CASE("Test eager relationship", "[query][entity][builder]") {
|
|||||||
|
|
||||||
auto q = eqb.build<department>();
|
auto q = eqb.build<department>();
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t02"."id", "t02"."first_name", "t02"."last_name", "t02"."dep_id" FROM "departments" "t01" LEFT JOIN "employees" "t02" ON "t01"."id" = "t02"."dep_id" ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t02"."id", "t02"."first_name", "t02"."last_name", "t02"."dep_id" FROM "departments" "t01" LEFT JOIN "employees" "t02" ON "t01"."id" = "t02"."dep_id" ORDER BY "t01"."id" ASC)";
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|
||||||
const auto& data = eqb.query_data();
|
// const auto& data = eqb.query_data();
|
||||||
|
|
||||||
|
|
||||||
// auto ctx = query::select(data.columns)
|
// auto ctx = query::select(data.columns)
|
||||||
@@ -401,7 +401,7 @@ TEST_CASE("Test has one lazy relationship", "[query][entity][builder][has_one][l
|
|||||||
|
|
||||||
auto q = eqb.build<user>();
|
auto q = eqb.build<user>();
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t01"."password" FROM "users" "t01" ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t01"."password" FROM "users" "t01" ORDER BY "t01"."id" ASC)";
|
||||||
|
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
@@ -421,7 +421,7 @@ TEST_CASE("Test has one eager relationship", "[query][entity][builder][has_one][
|
|||||||
|
|
||||||
auto q = eqb.build<country>();
|
auto q = eqb.build<country>();
|
||||||
REQUIRE(q.is_ok());
|
REQUIRE(q.is_ok());
|
||||||
const auto sql = q->str(db);
|
const auto sql = q->str(db.dialect());
|
||||||
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t02"."id", "t02"."name", "t02"."country_id" FROM "countries" "t01" LEFT JOIN "capitals" "t02" ON "t01"."id" = "t02"."country_id" ORDER BY "t01"."id" ASC)";
|
const std::string expected_sql = R"(SELECT "t01"."id", "t01"."name", "t02"."id", "t02"."name", "t02"."country_id" FROM "countries" "t01" LEFT JOIN "capitals" "t02" ON "t01"."id" = "t02"."country_id" ORDER BY "t01"."id" ASC)";
|
||||||
|
|
||||||
REQUIRE(expected_sql == sql);
|
REQUIRE(expected_sql == sql);
|
||||||
|
|||||||
Reference in New Issue
Block a user