implemented lazy loading for object_ptr and belongs_to
This commit is contained in:
@@ -3,92 +3,76 @@
|
||||
|
||||
#include "matador/object/primary_key_resolver.hpp"
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <mutex>
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class basic_object_proxy {
|
||||
public:
|
||||
virtual ~basic_object_proxy() = default;
|
||||
|
||||
[[nodiscard]] virtual void *get() const = 0;
|
||||
};
|
||||
|
||||
template < typename Type >
|
||||
class object_proxy;
|
||||
|
||||
template < typename Type >
|
||||
class object_resolver {
|
||||
public:
|
||||
virtual ~object_resolver() = default;
|
||||
|
||||
virtual Type* resolve(const object_proxy<Type> &proxy) const = 0;
|
||||
};
|
||||
|
||||
template < typename Type >
|
||||
class static_object_resolver final : public object_resolver<Type> {
|
||||
public:
|
||||
static_object_resolver() = default;
|
||||
explicit static_object_resolver(Type* obj)
|
||||
: obj_(obj) {}
|
||||
explicit static_object_resolver(std::unique_ptr<Type> obj)
|
||||
: obj_(std::move(obj)) {}
|
||||
|
||||
Type* resolve(const object_proxy<Type> &/*proxy*/) const override {
|
||||
return obj_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Type> obj_{};
|
||||
};
|
||||
|
||||
|
||||
template<class Type>
|
||||
class object_proxy final : public basic_object_proxy {
|
||||
class object_proxy final {
|
||||
public:
|
||||
object_proxy() = default;
|
||||
explicit object_proxy(Type* obj)
|
||||
: resolver_(std::make_unique<static_object_resolver<Type>>(obj))
|
||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {}
|
||||
|
||||
[[nodiscard]] void *get() const override { return static_cast<void*>(pointer()); }
|
||||
|
||||
Type* operator->() const { return pointer(); }
|
||||
Type& operator*() { return *pointer(); }
|
||||
const Type& operator*() const { return *pointer(); }
|
||||
|
||||
Type* pointer() const { return resolve(); }
|
||||
|
||||
Type& ref() { return *pointer(); }
|
||||
const Type& ref() const { return *pointer(); }
|
||||
|
||||
void reset(Type* obj) {
|
||||
pk_ = primary_key_resolver::resolve_object(*obj).pk;
|
||||
resolver_ = std::make_unique<static_object_resolver<Type>>(obj);
|
||||
// Lazy
|
||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, utils::identifier id)
|
||||
: resolver_(resolver)
|
||||
, pk_(std::move(id)) {
|
||||
}
|
||||
void reset(std::unique_ptr<Type> obj) {
|
||||
pk_ = primary_key_resolver::resolve_object(*obj).pk;
|
||||
resolver_ = std::make_unique<static_object_resolver<Type>>(std::move(obj));
|
||||
|
||||
// Eager
|
||||
object_proxy(std::weak_ptr<object_resolver<Type>> resolver, std::shared_ptr<Type> obj)
|
||||
: obj_(obj)
|
||||
, resolver_(resolver)
|
||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {
|
||||
}
|
||||
void reset(std::unique_ptr<object_resolver<Type>> &&resolver) {
|
||||
resolver_ = std::move(resolver);
|
||||
|
||||
// Transient
|
||||
explicit object_proxy(std::shared_ptr<Type> obj)
|
||||
: obj_(obj)
|
||||
, pk_(primary_key_resolver::resolve_object(*obj).pk) {
|
||||
}
|
||||
|
||||
[[nodiscard]] void *raw_pointer() const { return static_cast<void *>(pointer()); }
|
||||
|
||||
Type *operator->() { return pointer(); }
|
||||
Type &operator*() { return *pointer(); }
|
||||
const Type &operator*() const { return *pointer(); }
|
||||
|
||||
Type *pointer() const { return resolve(); }
|
||||
|
||||
[[nodiscard]] bool empty() const { return resolver_ == nullptr; }
|
||||
[[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]] const utils::identifier &primary_key() const { return pk_; }
|
||||
void primary_key(const utils::identifier &pk) { pk_ = pk; }
|
||||
|
||||
private:
|
||||
Type* resolve() const {
|
||||
return resolver_->resolve(*this);
|
||||
if (obj_) {
|
||||
return obj_.get();
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex_);
|
||||
auto resolver = resolver_.lock();
|
||||
if (!resolver) {
|
||||
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_);
|
||||
|
||||
return obj_.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<object_resolver<Type>> resolver_{std::make_unique<static_object_resolver<Type>>()};
|
||||
utils::identifier pk_{};
|
||||
std::shared_ptr<Type> obj_{};
|
||||
std::weak_ptr<object_resolver<Type>> resolver_{};
|
||||
utils::identifier pk_{};
|
||||
mutable std::mutex mutex_{};
|
||||
};
|
||||
|
||||
}
|
||||
#endif //OBJECT_PROXY_HPP
|
||||
|
||||
@@ -12,44 +12,53 @@ template <typename Type>
|
||||
class object_ptr {
|
||||
public:
|
||||
object_ptr()
|
||||
: ptr_(std::make_shared<object_proxy<Type>>()) {}
|
||||
explicit object_ptr(Type *obj)
|
||||
: ptr_(std::make_shared<object_proxy<Type>>(obj)) {}
|
||||
object_ptr(const object_ptr &other) : ptr_(other.ptr_) {}
|
||||
object_ptr(object_ptr &&other) noexcept : ptr_(std::move(other.ptr_)) {}
|
||||
: proxy_(std::make_shared<object_proxy<Type>>()) {}
|
||||
explicit object_ptr(std::shared_ptr<Type> obj)
|
||||
: proxy_(std::make_shared<object_proxy<Type>>(obj)) {}
|
||||
explicit object_ptr(std::shared_ptr<object_proxy<Type>> obj)
|
||||
: proxy_(std::move(obj)) {}
|
||||
object_ptr(const object_ptr &other) = default;
|
||||
object_ptr(object_ptr &&other) noexcept = default;
|
||||
object_ptr &operator=(const object_ptr &other) = default;
|
||||
object_ptr &operator=(object_ptr &&other) = default;
|
||||
|
||||
using value_type = Type;
|
||||
Type* operator->() const { return ptr_->pointer(); }
|
||||
Type& operator*() { return ptr_->ref(); }
|
||||
const Type& operator*() const { return ptr_->ref(); }
|
||||
bool operator==(const object_ptr &other) const {
|
||||
return get() == other.get();
|
||||
}
|
||||
bool operator!=(const object_ptr &other) const { return !operator==(other); }
|
||||
|
||||
[[nodiscard]] bool empty() const { return ptr_->pointer() == nullptr; }
|
||||
void reset(Type *obj) {
|
||||
ptr_->reset(obj);
|
||||
using value_type = Type;
|
||||
|
||||
Type *operator->() const { return get(); }
|
||||
Type &operator*() { return *get(); }
|
||||
const Type &operator*() const { return *get(); }
|
||||
|
||||
[[nodiscard]] bool empty() const { return get() == nullptr; }
|
||||
|
||||
Type *get() const {
|
||||
return proxy_ ? proxy_->pointer() : nullptr;
|
||||
}
|
||||
|
||||
Type* get() const { return static_cast<Type*>(ptr_->pointer()); }
|
||||
void reset() { proxy_.reset(); }
|
||||
|
||||
operator bool() { return valid(); }
|
||||
bool valid() { return ptr_ != nullptr; }
|
||||
bool valid() { return proxy_ != nullptr; }
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const { return proxy_->has_primary_key(); }
|
||||
[[nodiscard]] const utils::identifier &primary_key() const { return proxy_->primary_key(); }
|
||||
void primary_key(const utils::identifier &pk) { proxy_->primary_key(pk); }
|
||||
|
||||
[[nodiscard]] bool has_primary_key() const { return ptr_->has_primary_key(); }
|
||||
[[nodiscard]] const utils::identifier& primary_key() const { return ptr_->primary_key(); }
|
||||
void primary_key(const utils::identifier &pk) { ptr_->primary_key(pk); }
|
||||
private:
|
||||
std::shared_ptr<object_proxy<Type>> ptr_{};
|
||||
std::shared_ptr<object_proxy<Type> > proxy_{};
|
||||
};
|
||||
|
||||
template<typename>
|
||||
struct is_object_ptr : std::false_type
|
||||
{};
|
||||
struct is_object_ptr : std::false_type {
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct is_object_ptr<object_ptr<Type>> : std::true_type
|
||||
{};
|
||||
|
||||
struct is_object_ptr<object_ptr<Type> > : std::true_type {
|
||||
};
|
||||
}
|
||||
|
||||
#endif //OBJECT_PTR_HPP
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef MATADOR_OBJECT_LOADER_HPP
|
||||
#define MATADOR_OBJECT_LOADER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::utils {
|
||||
class identifier;
|
||||
}
|
||||
|
||||
namespace matador::object {
|
||||
|
||||
class abstract_object_resolver {
|
||||
public:
|
||||
virtual ~abstract_object_resolver() = default;
|
||||
|
||||
[[nodiscard]] const std::type_index& type() const { return type_; }
|
||||
|
||||
protected:
|
||||
explicit abstract_object_resolver(const std::type_index& ti) : type_(ti) {}
|
||||
|
||||
public:
|
||||
const std::type_index type_;
|
||||
};
|
||||
|
||||
|
||||
template<typename Type>
|
||||
class object_resolver : public abstract_object_resolver {
|
||||
public:
|
||||
object_resolver() : abstract_object_resolver(typeid(Type)) {}
|
||||
|
||||
virtual std::shared_ptr<Type> resolve(const utils::identifier& id) = 0;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBJECT_LOADER_HPP
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
#define MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::object {
|
||||
class object_resolver_factory {
|
||||
public:
|
||||
virtual ~object_resolver_factory() = default;
|
||||
|
||||
template<class Type>
|
||||
std::shared_ptr<object_resolver<Type>> resolver() {
|
||||
const auto res = acquire_resolver(std::type_index(typeid(Type)));
|
||||
if (!res) {
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
|
||||
return std::dynamic_pointer_cast<object_resolver<Type>>(res);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<abstract_object_resolver> acquire_resolver(const std::type_index &type) = 0;
|
||||
virtual void register_resolver(std::shared_ptr<abstract_object_resolver> &&resolver) = 0;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_OBJECT_RESOLVER_FACTORY_HPP
|
||||
@@ -5,7 +5,6 @@
|
||||
#include "matador/object/foreign_node_completer.hpp"
|
||||
#include "matador/object/many_to_many_relation.hpp"
|
||||
#include "matador/object/join_columns_collector.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
#include "matador/object/repository_node.hpp"
|
||||
|
||||
#include "matador/logger/log_manager.hpp"
|
||||
|
||||
+223
-173
@@ -2,8 +2,8 @@
|
||||
#define QUERY_SESSION_HPP
|
||||
|
||||
#include "matador/orm/error_code.hpp"
|
||||
#include "matador/orm/session_query_builder.hpp"
|
||||
|
||||
#include "matador/query/query_builder.hpp"
|
||||
#include "matador/query/criteria.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/generator.hpp"
|
||||
@@ -18,70 +18,83 @@
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
namespace matador::orm {
|
||||
|
||||
utils::error make_error(error_code ec, const std::string &msg);
|
||||
|
||||
struct session_context {
|
||||
session_context(utils::message_bus &bus, std::string dns, const size_t count, const size_t cache_size = 500)
|
||||
: bus(bus)
|
||||
, dns(std::move(dns))
|
||||
, connection_count(count)
|
||||
, cache_size(cache_size) {
|
||||
}
|
||||
utils::message_bus &bus;
|
||||
sql::connection_pool &pool;
|
||||
std::string dns;
|
||||
size_t connection_count{};
|
||||
size_t cache_size{500};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
class lazy_object_resolver final : public object::object_resolver<Type> {
|
||||
public:
|
||||
explicit lazy_object_resolver(sql::statement &&stmt) : stmt_(std::move(stmt)) {}
|
||||
|
||||
Type* resolve(const object::object_proxy<Type>& proxy) const override {
|
||||
return proxy.resolve();
|
||||
}
|
||||
|
||||
private:
|
||||
sql::statement stmt_;
|
||||
std::shared_ptr<sql::producer_resolver_factory> resolver_factory = std::make_shared<sql::producer_resolver_factory>();
|
||||
};
|
||||
|
||||
class prototype_builder final {
|
||||
public:
|
||||
explicit prototype_builder(const std::unordered_map<std::string, sql::statement> &statements_per_column)
|
||||
: statements_per_column_(statements_per_column) {}
|
||||
|
||||
|
||||
template < typename Type >
|
||||
Type build() const {
|
||||
Type obj;
|
||||
access::process(*this, obj);
|
||||
|
||||
return obj;
|
||||
: statements_per_column_(statements_per_column) {
|
||||
}
|
||||
|
||||
template < class V >
|
||||
static void on_primary_key(const char * /*id*/, V &/*pk*/, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {}
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
|
||||
template<typename Type>
|
||||
static void on_attribute(const char * /*id*/, Type &/*obj*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
Type build() const {
|
||||
Type obj;
|
||||
access::process(*this, obj);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<class V>
|
||||
static void on_primary_key(const char * /*id*/, V &/*pk*/,
|
||||
const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) {
|
||||
}
|
||||
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void on_attribute(const char * /*id*/, Type &/*obj*/,
|
||||
const utils::field_attributes &/*attr*/ = utils::null_attributes) {
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_belongs_to(const char *id, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
||||
const auto it = statements_per_column_.find(id);
|
||||
if (it == statements_per_column_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const auto it = statements_per_column_.find(id);
|
||||
if (it == statements_per_column_.end()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template<class Pointer>
|
||||
void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_one(const char * /*id*/, Pointer &/*obj*/, const utils::foreign_attributes &/*attr*/) {
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char * /*id*/, ContainerType &, const char * /*join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many(const char * /*id*/, ContainerType &, const char * /*join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/) {
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const char * /*join_column*/,
|
||||
const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
void on_has_many_to_many(const char * /*id*/, ContainerType &/*cont*/, const utils::foreign_attributes &/*attr*/) {
|
||||
}
|
||||
|
||||
private:
|
||||
const std::unordered_map<std::string, sql::statement> &statements_per_column_;
|
||||
};
|
||||
|
||||
class session final : public sql::executor {
|
||||
public:
|
||||
session(session_context &&ctx, const query::schema &scm);
|
||||
@@ -95,8 +108,10 @@ public:
|
||||
*/
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> insert(Type *obj);
|
||||
template< class Type, typename... Args >
|
||||
utils::result<object::object_ptr<Type>, utils::error> insert(Args&&... args);
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> insert(object::object_ptr<Type> obj);
|
||||
template<class Type, typename... Args>
|
||||
utils::result<object::object_ptr<Type>, utils::error> insert(Args &&... args);
|
||||
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> update(const object::object_ptr<Type> &obj);
|
||||
@@ -112,33 +127,40 @@ public:
|
||||
utils::result<void, utils::error> drop_table() const;
|
||||
utils::result<void, utils::error> drop_table(const std::string &table_name) const;
|
||||
|
||||
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::query_context &q) const;
|
||||
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error>
|
||||
fetch_all(const sql::query_context &q) const;
|
||||
[[nodiscard]] utils::result<size_t, utils::error> execute(const std::string &sql) const;
|
||||
|
||||
[[nodiscard]] std::vector<object::attribute> describe_table(const std::string &table_name) const;
|
||||
[[nodiscard]] bool table_exists(const std::string &table_name) const;
|
||||
|
||||
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(const sql::query_context &ctx) const override;
|
||||
[[nodiscard]] utils::result<std::unique_ptr<sql::query_result_impl>, utils::error> fetch(
|
||||
const sql::query_context &ctx) const override;
|
||||
[[nodiscard]] utils::result<size_t, utils::error> execute(const sql::query_context &ctx) const override;
|
||||
[[nodiscard]] utils::result<sql::statement, utils::error> prepare(const sql::query_context &ctx) override;
|
||||
[[nodiscard]] std::string str( const sql::query_context& ctx ) const override;
|
||||
[[nodiscard]] const sql::dialect& dialect() const override;
|
||||
[[nodiscard]] std::string str(const sql::query_context &ctx) const override;
|
||||
[[nodiscard]] const sql::dialect &dialect() const override;
|
||||
[[nodiscard]] std::shared_ptr<sql::producer_resolver_factory> resolver_factory() const override;
|
||||
[[nodiscard]] const class query::basic_schema &schema() const;
|
||||
|
||||
private:
|
||||
friend class query_select;
|
||||
|
||||
static query::fetchable_query build_select_query(entity_query_data &&data);
|
||||
static query::fetchable_query build_select_query(query::entity_query_data &&data);
|
||||
|
||||
private:
|
||||
sql::connection_pool pool_;
|
||||
mutable sql::statement_cache cache_;
|
||||
const sql::dialect &dialect_;
|
||||
|
||||
const query::schema& schema_;
|
||||
mutable std::unordered_map<std::string, std::vector<object::attribute>> prototypes_;
|
||||
const query::basic_schema &schema_;
|
||||
mutable std::unordered_map<std::string, std::vector<object::attribute> > prototypes_;
|
||||
std::shared_ptr<sql::producer_resolver_factory> resolver_factory_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(Type *obj) {
|
||||
std::shared_ptr<Type> ptr(obj);
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
@@ -149,171 +171,199 @@ utils::result<object::object_ptr<Type>, utils::error> session::insert(Type *obj)
|
||||
.values(query::generator::placeholders<Type>())
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
|
||||
if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) {
|
||||
return utils::failure(insert_result.err());
|
||||
}
|
||||
return utils::ok(object::object_ptr{obj});
|
||||
return utils::ok(object::object_ptr{ptr});
|
||||
}
|
||||
|
||||
template<class Type, typename ... Args>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::insert( Args&&... args ) {
|
||||
return insert(new Type(std::forward<Args>(args)...));
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(object::object_ptr<Type> obj) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
|
||||
auto res = query::query::insert()
|
||||
.into(it->second.name(), query::generator::columns<Type>(schema_))
|
||||
.values(query::generator::placeholders<Type>())
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
|
||||
if (const auto insert_result = res->bind(*obj).execute(); !insert_result.is_ok()) {
|
||||
return utils::failure(insert_result.err());
|
||||
}
|
||||
return utils::ok(obj);
|
||||
}
|
||||
|
||||
template<class Type, typename... Args>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::insert(Args &&... args) {
|
||||
return insert(new Type(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
class pk_object_binder final {
|
||||
public:
|
||||
explicit pk_object_binder(sql::statement &stmt, size_t position)
|
||||
: stmt_(stmt)
|
||||
, binding_position_(position){}
|
||||
explicit pk_object_binder(sql::statement &stmt, const size_t position)
|
||||
: stmt_(stmt)
|
||||
, binding_position_(position) {
|
||||
}
|
||||
|
||||
template < class Type >
|
||||
sql::statement& bind(Type &obj) {
|
||||
access::process(*this, obj);
|
||||
return stmt_;
|
||||
}
|
||||
template<class Type>
|
||||
sql::statement &bind(Type &obj) {
|
||||
access::process(*this, obj);
|
||||
return stmt_;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void on_primary_key(const char * /*id*/, Type &x, const utils::primary_key_attribute& /*attr*/ = utils::default_pk_attributes) {
|
||||
stmt_.bind(binding_position_, x);
|
||||
}
|
||||
template<class Type>
|
||||
void on_primary_key(const char * /*id*/, Type &x, const utils::primary_key_attribute & /*attr*/ = utils::default_pk_attributes) {
|
||||
stmt_.bind(binding_position_, x);
|
||||
}
|
||||
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
template < class Type >
|
||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template < class Pointer >
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
template < class Pointer >
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const char * /*join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const char * /*join_column*/,
|
||||
const char * /*inverse_join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class Type>
|
||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template<class Pointer>
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
template<class Pointer>
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const char * /*join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/ = utils::CascadeNoneFetchLazy) {}
|
||||
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const char * /*join_column*/,
|
||||
const char * /*inverse_join_column*/,
|
||||
const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/,
|
||||
ContainerType &/*c*/,
|
||||
const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
sql::statement &stmt_;
|
||||
size_t binding_position_{0};
|
||||
sql::object_pk_binder pk_binder_;
|
||||
sql::statement &stmt_;
|
||||
size_t binding_position_{0};
|
||||
sql::object_pk_binder pk_binder_{};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::update( const object::object_ptr<Type>& obj ) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
using namespace matador::utils;
|
||||
using namespace matador::query;
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::update(const object::object_ptr<Type> &obj) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
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());
|
||||
auto res = matador::query::query::update(it->second.name())
|
||||
.set(generator::column_value_pairs<Type>())
|
||||
.where(col == _)
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
||||
auto res = matador::query::query::update(it->second.name())
|
||||
.set(generator::column_value_pairs<Type>())
|
||||
.where(col == _)
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
|
||||
res->bind(*obj);
|
||||
pk_object_binder binder(res.value(), res->bind_pos());
|
||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
||||
return utils::failure(update_result.err());
|
||||
}
|
||||
return utils::ok(object::object_ptr{obj});
|
||||
res->bind(*obj);
|
||||
pk_object_binder binder(res.value(), res->bind_pos());
|
||||
if (const auto update_result = binder.bind(*obj).execute(); !update_result.is_ok()) {
|
||||
return utils::failure(update_result.err());
|
||||
}
|
||||
return utils::ok(object::object_ptr{obj});
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
utils::result<void, utils::error> session::remove( const object::object_ptr<Type>& obj ) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
using namespace matador::utils;
|
||||
using namespace matador::query;
|
||||
utils::result<void, utils::error> session::remove(const object::object_ptr<Type> &obj) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
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());
|
||||
auto res = matador::query::query::remove()
|
||||
.from( it->second.name() )
|
||||
.where(col == _)
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
const auto col = table_column(it->second.node().info().primary_key_attribute()->name());
|
||||
auto res = matador::query::query::remove()
|
||||
.from(it->second.name())
|
||||
.where(col == _)
|
||||
.prepare(*this);
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
|
||||
pk_object_binder binder(res.value(), res->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>();
|
||||
pk_object_binder binder(res.value(), res->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>
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::find(const PrimaryKeyType& pk) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
const auto& info = it->second.node().info();
|
||||
// if (!info) {
|
||||
// return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
// }
|
||||
if (!info.has_primary_key()) {
|
||||
return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key."));
|
||||
}
|
||||
utils::result<object::object_ptr<Type>, utils::error> session::find(const PrimaryKeyType &pk) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
const auto &info = it->second.node().info();
|
||||
if (!info.has_primary_key()) {
|
||||
return utils::failure(make_error(error_code::NoPrimaryKey, "Type hasn't primary key."));
|
||||
}
|
||||
|
||||
session_query_builder eqb(schema_, *this);
|
||||
const query::table_column c(&it->second.table(),info.primary_key_attribute()->name());
|
||||
using namespace matador::query;
|
||||
auto data = eqb.build<Type>(c == utils::_);
|
||||
if (!data.is_ok()) {
|
||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + info.name() + "."));
|
||||
}
|
||||
query::query_builder eqb(schema_, *this);
|
||||
const query::table_column c(&it->second.table(), info.primary_key_attribute()->name());
|
||||
using namespace matador::query;
|
||||
auto data = eqb.build<Type>(c == utils::_);
|
||||
if (!data.is_ok()) {
|
||||
return utils::failure(make_error(error_code::FailedToBuildQuery,
|
||||
"Failed to build query for type " + info.name() + "."));
|
||||
}
|
||||
|
||||
auto res = build_select_query(data.release()).prepare(*this);
|
||||
auto res = build_select_query(data.release()).prepare(*this);
|
||||
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType&>(pk)).template fetch_one<Type>();
|
||||
if (stmt_result && !stmt_result.value()) {
|
||||
return utils::failure(make_error(error_code::FailedToFindObject, "Failed to find object of type " + info.name() + " with primary key " + std::to_string(pk) + "."));
|
||||
}
|
||||
return utils::ok(object::object_ptr<Type>{ stmt_result->release() });
|
||||
if (!res) {
|
||||
return utils::failure(res.err());
|
||||
}
|
||||
auto stmt_result = res->bind(0, const_cast<PrimaryKeyType &>(pk))
|
||||
.template fetch_one<Type>();
|
||||
if (!stmt_result) {
|
||||
return utils::failure(make_error(error_code::FailedToFindObject,
|
||||
"Failed to find object of type " + info.name() + " with primary key " +
|
||||
std::to_string(pk) + "."));
|
||||
}
|
||||
return utils::ok(*stmt_result);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
utils::result<sql::query_result<Type>, utils::error> session::find(query::criteria_ptr clause) {
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
const auto it = schema_.find(typeid(Type));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(make_error(error_code::UnknownType, "Failed to determine requested type."));
|
||||
}
|
||||
|
||||
session_query_builder eqb(schema_, *this);
|
||||
auto data = eqb.build<Type>(std::move(clause));
|
||||
if (!data.is_ok()) {
|
||||
return utils::failure(make_error(error_code::FailedToBuildQuery, "Failed to build query for type " + it->second.name() + "."));
|
||||
}
|
||||
query::query_builder eqb(schema_, *this);
|
||||
auto data = eqb.build<Type>(std::move(clause));
|
||||
if (!data.is_ok()) {
|
||||
return utils::failure(make_error(error_code::FailedToBuildQuery,
|
||||
"Failed to build query for type " + it->second.name() + "."));
|
||||
}
|
||||
|
||||
auto result = build_select_query(data.release()).prepare(*this);
|
||||
if (!result.is_ok()) {
|
||||
return utils::failure(result.err());
|
||||
}
|
||||
auto result = build_select_query(data.release()).prepare(*this);
|
||||
if (!result.is_ok()) {
|
||||
return utils::failure(result.err());
|
||||
}
|
||||
|
||||
return result->template fetch<Type>();
|
||||
return result->template fetch<Type>();
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef MATADOR_BASIC_SCHEMA_HPP
|
||||
#define MATADOR_BASIC_SCHEMA_HPP
|
||||
|
||||
#include "matador/object/repository.hpp"
|
||||
|
||||
#include "matador/sql/producer_resolver_factory.hpp"
|
||||
|
||||
#include "matador/query/table.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::query {
|
||||
class schema_node final {
|
||||
public:
|
||||
schema_node(class table tab, const object::repository_node& node);
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
[[nodiscard]] const class table& table() const;
|
||||
[[nodiscard]] const object::repository_node& node() const;
|
||||
|
||||
private:
|
||||
class table table_;
|
||||
const object::repository_node& node_;
|
||||
};
|
||||
|
||||
class basic_schema {
|
||||
public:
|
||||
using schema_node_map = std::unordered_map<std::type_index, schema_node>;
|
||||
using iterator = std::unordered_map<std::type_index, schema_node>::iterator;
|
||||
using const_iterator = std::unordered_map<std::type_index, schema_node>::const_iterator;
|
||||
|
||||
basic_schema();
|
||||
explicit basic_schema(const std::string &name);
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
[[nodiscard]] const_iterator begin() const;
|
||||
[[nodiscard]] const_iterator end() const;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
[[nodiscard]] bool empty() const;
|
||||
|
||||
iterator find(const std::type_index& ti);
|
||||
iterator find(const std::string& name);
|
||||
|
||||
[[nodiscard]] const_iterator find(const std::type_index& ti) const;
|
||||
[[nodiscard]] const_iterator find(const std::string& name) const;
|
||||
|
||||
[[nodiscard]] bool contains(const std::type_index &index) const;
|
||||
|
||||
void initialize_executor(sql::executor &exec) const;
|
||||
|
||||
protected:
|
||||
template<typename Type>
|
||||
friend class schema_observer;
|
||||
|
||||
object::repository repo_;
|
||||
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
||||
std::unordered_map<std::type_index, std::unique_ptr<sql::resolver_producer>> resolver_producers_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_BASIC_SCHEMA_HPP
|
||||
@@ -7,6 +7,9 @@
|
||||
#include "matador/utils/placeholder.hpp"
|
||||
#include "matador/utils/value.hpp"
|
||||
|
||||
namespace matador::utils {
|
||||
class identifier;
|
||||
}
|
||||
namespace matador::sql {
|
||||
struct query_context;
|
||||
}
|
||||
@@ -57,6 +60,9 @@ criteria_ptr operator>=(const table_column &col, utils::placeholder p);
|
||||
criteria_ptr operator<(const table_column &col, utils::placeholder p);
|
||||
criteria_ptr operator<=(const table_column &col, utils::placeholder p);
|
||||
|
||||
criteria_ptr operator==(const table_column &col, const utils::identifier &id);
|
||||
criteria_ptr operator!=(const table_column &col, const utils::identifier &id);
|
||||
|
||||
criteria_ptr operator&&(criteria_ptr left, criteria_ptr right);
|
||||
|
||||
criteria_ptr operator||(criteria_ptr left, criteria_ptr right);
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#include "matador/query/internal/column_value_pair.hpp"
|
||||
|
||||
#include "matador/query/table.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
#include "matador/query/basic_schema.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -45,7 +45,7 @@ constexpr auto default_column_generator_options = column_generator_options::Forc
|
||||
|
||||
class column_generator {
|
||||
public:
|
||||
explicit column_generator(const schema &repo,
|
||||
explicit column_generator(const basic_schema &repo,
|
||||
const table* tab,
|
||||
column_generator_options options = default_column_generator_options);
|
||||
|
||||
@@ -141,7 +141,7 @@ private:
|
||||
void push(const std::string &column_name);
|
||||
|
||||
private:
|
||||
const schema &repo_;
|
||||
const basic_schema &repo_;
|
||||
std::vector<table_column> result_;
|
||||
std::stack<const table*> table_stack_;
|
||||
std::unordered_set<std::string> seen_tables;
|
||||
@@ -289,7 +289,7 @@ std::vector<internal::column_value_pair> column_value_pairs() {
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
std::vector<table_column> columns(const schema &repo,
|
||||
std::vector<table_column> columns(const basic_schema &repo,
|
||||
const table &tab,
|
||||
const column_generator_options options = default_column_generator_options) {
|
||||
column_generator generator(repo, &tab, options);
|
||||
@@ -297,7 +297,7 @@ std::vector<table_column> columns(const schema &repo,
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
std::vector<table_column> columns(const schema &repo,
|
||||
std::vector<table_column> columns(const basic_schema &repo,
|
||||
const column_generator_options options = default_column_generator_options) {
|
||||
const auto it = repo.find(typeid(Type));
|
||||
if (it == repo.end()) {
|
||||
@@ -309,7 +309,7 @@ std::vector<table_column> columns(const schema &repo,
|
||||
|
||||
template<typename Type>
|
||||
std::vector<table_column> columns(const Type &obj,
|
||||
const schema &repo,
|
||||
const basic_schema &repo,
|
||||
const column_generator_options options = default_column_generator_options) {
|
||||
const auto it = repo.find(typeid(Type));
|
||||
if (it == repo.end()) {
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
|
||||
#include "query_intermediate.hpp"
|
||||
|
||||
#include "../query_compiler.hpp"
|
||||
#include "matador/query/query_compiler.hpp"
|
||||
#include "matador/sql/executor.hpp"
|
||||
|
||||
#include "../../sql/query_result.hpp"
|
||||
#include "../../sql/record.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
#include "matador/sql/query_record_result.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include "../../utils/error.hpp"
|
||||
#include "../../utils/result.hpp"
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class executor;
|
||||
@@ -32,14 +34,15 @@ public:
|
||||
}
|
||||
|
||||
const auto prototype = result.value()->prototype();
|
||||
return utils::ok(sql::query_result<Type>(result.release(), [prototype] {
|
||||
auto resolver = exec.resolver_factory()->resolver<Type>();
|
||||
return utils::ok(sql::query_result<Type>(result.release(), resolver, [prototype] {
|
||||
return sql::detail::create_prototype<Type>(prototype);
|
||||
}));
|
||||
}
|
||||
[[nodiscard]] utils::result<sql::query_result<sql::record>, utils::error> fetch_all(const sql::executor &exec) const;
|
||||
|
||||
template < class Type >
|
||||
utils::result<std::unique_ptr<Type>, utils::error> fetch_one(const sql::executor &exec)
|
||||
utils::result<object::object_ptr<Type>, utils::error> fetch_one(sql::executor &exec)
|
||||
{
|
||||
auto result = fetch(exec);
|
||||
if (!result.is_ok()) {
|
||||
@@ -47,15 +50,16 @@ public:
|
||||
}
|
||||
|
||||
const auto prototype = result.value()->prototype();
|
||||
auto objects = sql::query_result<Type>(result.release(), [prototype] {
|
||||
auto resolver = exec.resolver_factory()->resolver<Type>();
|
||||
auto objects = sql::query_result<Type>(result.release(), resolver, [prototype] {
|
||||
return sql::detail::create_prototype<Type>(prototype);
|
||||
});
|
||||
auto first = objects.begin();
|
||||
if (first == objects.end()) {
|
||||
return utils::ok(std::unique_ptr<Type>{nullptr});
|
||||
return utils::ok(object::object_ptr<Type>{});
|
||||
}
|
||||
|
||||
return utils::ok(std::unique_ptr<Type>{first.release()});
|
||||
return utils::ok(first.optr());
|
||||
}
|
||||
|
||||
[[nodiscard]] utils::result<std::optional<sql::record>, utils::error> fetch_one(const sql::executor &exec) const;
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
|
||||
#include "matador/query/intermediates/query_intermediate.hpp"
|
||||
#include "matador/query/intermediates/query_into_intermediate.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
#include "matador/query/generator.hpp"
|
||||
|
||||
namespace matador::query {
|
||||
class schema;
|
||||
|
||||
class query_insert_intermediate : public query_intermediate {
|
||||
public:
|
||||
|
||||
@@ -4,13 +4,12 @@
|
||||
#include "matador/query/query_intermediates.hpp"
|
||||
|
||||
#include "matador/query/generator.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class connection;
|
||||
}
|
||||
namespace matador::query {
|
||||
|
||||
class schema;
|
||||
table_column alias(const std::string &column, const std::string &as);
|
||||
table_column alias(table_column &&col, const std::string &as);
|
||||
table_column count(const std::string &column);
|
||||
|
||||
+43
-45
@@ -1,13 +1,11 @@
|
||||
#ifndef QUERY_ENTITY_QUERY_BUILDER_HPP
|
||||
#define QUERY_ENTITY_QUERY_BUILDER_HPP
|
||||
|
||||
#include "matador/orm/query_builder_exception.hpp"
|
||||
#include "matador/query/query_builder_exception.hpp"
|
||||
|
||||
#include "matador/query/criteria.hpp"
|
||||
#include "matador/query/query.hpp"
|
||||
#include "matador/query/schema.hpp"
|
||||
|
||||
#include "matador/sql/executor.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/object/join_columns_collector.hpp"
|
||||
@@ -21,45 +19,45 @@
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::orm {
|
||||
namespace matador::query {
|
||||
|
||||
struct entity_query_data {
|
||||
const query::table* root_table{nullptr};
|
||||
const table* root_table{nullptr};
|
||||
std::string pk_column_name{};
|
||||
std::vector<query::table_column> columns{};
|
||||
std::vector<table_column> columns{};
|
||||
std::unordered_map<std::string, sql::statement> lazy_loading_statements{};
|
||||
std::vector<query::join_data> joins{};
|
||||
query::criteria_ptr where_clause{};
|
||||
std::vector<join_data> joins{};
|
||||
criteria_ptr where_clause{};
|
||||
};
|
||||
|
||||
class criteria_transformer final : public query::criteria_visitor {
|
||||
class criteria_transformer final : public criteria_visitor {
|
||||
public:
|
||||
criteria_transformer(const query::schema &repo, const std::unordered_map<std::string, query::table>& tables_by_name);
|
||||
void visit( const query::between_criteria& node ) override;
|
||||
void visit( const query::binary_criteria& node ) override;
|
||||
void visit( const query::binary_column_criteria& node ) override;
|
||||
void visit( const query::collection_criteria& node ) override;
|
||||
void visit( const query::collection_query_criteria& node ) override;
|
||||
void visit( const query::like_criteria& node ) override;
|
||||
void visit( const query::logical_criteria& node ) override;
|
||||
void visit( const query::not_criteria& node ) override;
|
||||
criteria_transformer(const basic_schema &repo, const std::unordered_map<std::string, table>& tables_by_name);
|
||||
void visit( const between_criteria& node ) override;
|
||||
void visit( const binary_criteria& node ) override;
|
||||
void visit( const binary_column_criteria& node ) override;
|
||||
void visit( const collection_criteria& node ) override;
|
||||
void visit( const collection_query_criteria& node ) override;
|
||||
void visit( const like_criteria& node ) override;
|
||||
void visit( const logical_criteria& node ) override;
|
||||
void visit( const not_criteria& node ) override;
|
||||
|
||||
private:
|
||||
void update_criteria_column(const query::abstract_column_criteria& node) const;
|
||||
void update_criteria_column(const abstract_column_criteria& node) const;
|
||||
|
||||
private:
|
||||
const query::schema &repo_;
|
||||
const std::unordered_map<std::string, query::table>& tables_by_name_;
|
||||
const basic_schema &repo_;
|
||||
const std::unordered_map<std::string, table>& tables_by_name_;
|
||||
};
|
||||
|
||||
class session_query_builder final {
|
||||
class query_builder final {
|
||||
public:
|
||||
session_query_builder(const query::schema &scm, sql::executor &exec)
|
||||
query_builder(const basic_schema &scm, sql::executor &exec)
|
||||
: schema_(scm)
|
||||
, executor_(exec){}
|
||||
|
||||
template<class EntityType>
|
||||
utils::result<entity_query_data, query_build_error> build(query::criteria_ptr clause = {}) {
|
||||
utils::result<entity_query_data, query_build_error> build(criteria_ptr clause = {}) {
|
||||
const auto it = schema_.find(typeid(EntityType));
|
||||
if (it == schema_.end()) {
|
||||
return utils::failure(query_build_error::UnknownType);
|
||||
@@ -144,8 +142,8 @@ public:
|
||||
}
|
||||
|
||||
append_join(
|
||||
query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
query::table_column{&next->second, join_column}
|
||||
table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
table_column{&next->second, join_column}
|
||||
);
|
||||
|
||||
|
||||
@@ -159,7 +157,7 @@ public:
|
||||
// if (next != processed_tables_.end()) {
|
||||
// return;
|
||||
// }
|
||||
// table_info_stack_.push({*result, std::make_shared<query::table>(info.name(), build_alias('t', ++table_index))});
|
||||
// table_info_stack_.push({*result, std::make_shared<table>(info.name(), build_alias('t', ++table_index))});
|
||||
// next = processed_tables_.insert({info.name(), table_info_stack_.top().table}).first;
|
||||
// typename ContainerType::value_type::value_type obj;
|
||||
// access::process(*this , obj);
|
||||
@@ -170,8 +168,8 @@ public:
|
||||
// }
|
||||
//
|
||||
// append_join(
|
||||
// query::column{table_info_stack_.top().table.get(), table_info_stack_.top().info.get().primary_key_attribute()->name()},
|
||||
// query::column{next->second.get(), join_column}
|
||||
// column{table_info_stack_.top().table.get(), table_info_stack_.top().info.get().primary_key_attribute()->name()},
|
||||
// column{next->second.get(), join_column}
|
||||
// );
|
||||
}
|
||||
|
||||
@@ -210,12 +208,12 @@ public:
|
||||
}
|
||||
|
||||
append_join(
|
||||
query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
query::table_column{&relation->second, join_column}
|
||||
table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
table_column{&relation->second, join_column}
|
||||
);
|
||||
append_join(
|
||||
query::table_column{&relation->second, inverse_join_column},
|
||||
query::table_column{&next->second, result->second.node().info().primary_key_attribute()->name()}
|
||||
table_column{&relation->second, inverse_join_column},
|
||||
table_column{&next->second, result->second.node().info().primary_key_attribute()->name()}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -257,12 +255,12 @@ public:
|
||||
const auto join_columns = join_columns_collector_.collect<typename ContainerType::value_type::value_type>();
|
||||
|
||||
append_join(
|
||||
query::table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
query::table_column{&relation->second, join_columns.inverse_join_column}
|
||||
table_column{&table_info_stack_.top().table, table_info_stack_.top().info.primary_key_attribute()->name()},
|
||||
table_column{&relation->second, join_columns.inverse_join_column}
|
||||
);
|
||||
append_join(
|
||||
query::table_column{&relation->second, join_columns.join_column},
|
||||
query::table_column{&next->second, result->second.node().info().primary_key_attribute()->name()}
|
||||
table_column{&relation->second, join_columns.join_column},
|
||||
table_column{&next->second, result->second.node().info().primary_key_attribute()->name()}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -272,17 +270,17 @@ private:
|
||||
void push(const std::string &column_name);
|
||||
static std::string build_alias(char prefix, unsigned int count);
|
||||
[[nodiscard]] bool is_root_entity() const;
|
||||
void append_join(const query::table_column &left, const query::table_column &right);
|
||||
void append_join(const table_column &left, const table_column &right);
|
||||
|
||||
private:
|
||||
struct table_info {
|
||||
const object::basic_object_info &info;
|
||||
query::table table;
|
||||
class table table;
|
||||
};
|
||||
|
||||
std::stack<table_info> table_info_stack_{};
|
||||
std::unordered_map<std::string, query::table> processed_tables_{};
|
||||
const query::schema &schema_;
|
||||
std::unordered_map<std::string, table> processed_tables_{};
|
||||
const basic_schema &schema_;
|
||||
entity_query_data entity_query_data_{};
|
||||
unsigned int column_index{0};
|
||||
unsigned int table_index{0};
|
||||
@@ -291,7 +289,7 @@ private:
|
||||
};
|
||||
|
||||
template<class Pointer>
|
||||
void session_query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) {
|
||||
void query_builder::on_foreign_object(const char *id, Pointer &, const utils::foreign_attributes &attr) {
|
||||
const auto it = schema_.find(typeid(typename Pointer::value_type));
|
||||
if (it == schema_.end()) {
|
||||
throw query_builder_exception{query_build_error::UnknownType};
|
||||
@@ -315,15 +313,15 @@ void session_query_builder::on_foreign_object(const char *id, Pointer &, const u
|
||||
table_info_stack_.pop();
|
||||
|
||||
append_join(
|
||||
query::table_column{&table_info_stack_.top().table, id},
|
||||
query::table_column{&next->second, info.primary_key_attribute()->name()}
|
||||
table_column{&table_info_stack_.top().table, id},
|
||||
table_column{&next->second, info.primary_key_attribute()->name()}
|
||||
);
|
||||
} else {
|
||||
push(id);
|
||||
using namespace matador::utils;
|
||||
using namespace matador::query;
|
||||
// create select query
|
||||
auto result = matador::query::query::select(generator::columns<typename Pointer::value_type>(schema_, foreign_table, generator::column_generator_options::ForceLazy))
|
||||
auto result = query::query::select(generator::columns<typename Pointer::value_type>(schema_, foreign_table, generator::column_generator_options::ForceLazy))
|
||||
.from(foreign_table)
|
||||
.where(table_column(&foreign_table, info.primary_key_attribute()->name(), "") == _)
|
||||
.prepare(executor_);
|
||||
+2
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace matador::orm {
|
||||
namespace matador::query {
|
||||
|
||||
enum class query_build_error : std::uint8_t {
|
||||
Ok = 0,
|
||||
@@ -17,7 +17,7 @@ enum class query_build_error : std::uint8_t {
|
||||
|
||||
class query_builder_exception final : public std::exception {
|
||||
public:
|
||||
explicit query_builder_exception(const query_build_error error, utils::error &&err = {});
|
||||
explicit query_builder_exception(query_build_error error, utils::error &&err = {});
|
||||
|
||||
[[nodiscard]] query_build_error error_type() const;
|
||||
[[nodiscard]] const utils::error &error() const;
|
||||
@@ -0,0 +1,62 @@
|
||||
#ifndef MATADOR_QUERY_OBJECT_LOADER_HPP
|
||||
#define MATADOR_QUERY_OBJECT_LOADER_HPP
|
||||
|
||||
#include "matador/sql/internal/identifier_statement_binder.hpp"
|
||||
#include "matador/sql/statement.hpp"
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include "matador/query/table.hpp"
|
||||
#include "matador/query/query_builder.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class executor;
|
||||
}
|
||||
|
||||
namespace matador::query {
|
||||
template<typename Type>
|
||||
class query_object_resolver : public object::object_resolver<Type> {
|
||||
public:
|
||||
explicit query_object_resolver(const basic_schema &repo, sql::executor& exec, const table &tab, std::string pk_name)
|
||||
: executor_(exec)
|
||||
, schema_(repo)
|
||||
, table_(tab)
|
||||
, pk_name_(std::move(pk_name)) {}
|
||||
|
||||
std::shared_ptr<Type> resolve(const utils::identifier &id) override;
|
||||
protected:
|
||||
sql::executor& executor_;
|
||||
const basic_schema &schema_;
|
||||
const table &table_;
|
||||
std::string pk_name_;
|
||||
std::type_index index{typeid(Type)};
|
||||
};
|
||||
|
||||
utils::result<sql::statement, utils::error> prepare_statement(sql::executor& exec, entity_query_data &&data);
|
||||
|
||||
template<typename Type>
|
||||
std::shared_ptr<Type> query_object_resolver<Type>::resolve(const utils::identifier &id) {
|
||||
query_builder qb(schema_, executor_);
|
||||
const auto *pk_column = table_[pk_name_];
|
||||
auto builder_result = qb.build<Type>(*pk_column == utils::_);
|
||||
|
||||
if (!builder_result) {
|
||||
return nullptr;
|
||||
}
|
||||
auto stmt = prepare_statement(executor_, builder_result.release());
|
||||
if (!stmt) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sql::identifier_statement_binder binder(*stmt);
|
||||
binder.bind(id);
|
||||
|
||||
auto result = stmt->template fetch_one_raw<Type>();
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
return *result;
|
||||
}
|
||||
|
||||
}
|
||||
#endif //MATADOR_QUERY_OBJECT_LOADER_HPP
|
||||
@@ -2,19 +2,41 @@
|
||||
#define MATADOR_SCHEMA_HPP
|
||||
|
||||
#include "matador/object/observer.hpp"
|
||||
#include "matador/object/repository.hpp"
|
||||
|
||||
#include "matador/sql/query_context.hpp"
|
||||
#include "matador/sql/internal/resolver_producer.hpp"
|
||||
|
||||
#include "matador/query/table.hpp"
|
||||
#include "matador/query/query_object_resolver.hpp"
|
||||
#include "matador/query/basic_schema.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class connection_pool;
|
||||
class connection;
|
||||
class executor;
|
||||
}
|
||||
|
||||
namespace matador::query {
|
||||
|
||||
template<typename Type>
|
||||
class query_resolver_producer : public sql::resolver_producer {
|
||||
public:
|
||||
query_resolver_producer() = default;
|
||||
query_resolver_producer(const basic_schema& repo, const table& tab, std::string pk_name)
|
||||
: resolver_producer(typeid(Type))
|
||||
, repo_(repo)
|
||||
, table_(tab)
|
||||
, pk_name_(std::move(pk_name)) {}
|
||||
|
||||
std::shared_ptr<object::abstract_object_resolver> produce(sql::executor &exec) override {
|
||||
return std::make_shared<query_object_resolver<Type>>(repo_, exec, table_, std::move(pk_name_));
|
||||
}
|
||||
|
||||
private:
|
||||
const basic_schema& repo_;
|
||||
const table& table_;
|
||||
std::string pk_name_;
|
||||
};
|
||||
|
||||
class schema;
|
||||
|
||||
using schema_ref = std::reference_wrapper<schema>;
|
||||
@@ -52,25 +74,14 @@ private:
|
||||
schema& schema_;
|
||||
};
|
||||
|
||||
class schema_node final {
|
||||
class schema final : public basic_schema {
|
||||
public:
|
||||
schema_node(class table tab, const object::repository_node& node);
|
||||
using basic_schema::basic_schema;
|
||||
using basic_schema::iterator;
|
||||
using basic_schema::const_iterator;
|
||||
using basic_schema::contains;
|
||||
using basic_schema::find;
|
||||
|
||||
[[nodiscard]] const std::string& name() const;
|
||||
[[nodiscard]] const class table& table() const;
|
||||
[[nodiscard]] const object::repository_node& node() const;
|
||||
|
||||
private:
|
||||
class table table_;
|
||||
const object::repository_node& node_;
|
||||
};
|
||||
class schema final {
|
||||
public:
|
||||
using iterator = std::unordered_map<std::type_index, schema_node>::iterator;
|
||||
using const_iterator = std::unordered_map<std::type_index, schema_node>::const_iterator;
|
||||
|
||||
schema() = default;
|
||||
explicit schema(const std::string &name);
|
||||
|
||||
template<typename Type, typename... Observers>
|
||||
[[nodiscard]] utils::result<void, utils::error> attach(const std::string &name, Observers&&... observers) {
|
||||
@@ -97,28 +108,15 @@ public:
|
||||
[[nodiscard]] static utils::result<std::vector<object::attribute>, utils::error> describe_table(const std::string &table_name, const sql::connection &conn) ;
|
||||
[[nodiscard]] utils::result<bool, utils::error> table_exists(const std::string &table_name, const sql::connection &conn) const;
|
||||
|
||||
iterator begin();
|
||||
iterator end();
|
||||
const_iterator begin() const;
|
||||
const_iterator end() const;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
[[nodiscard]] bool empty() const;
|
||||
template<typename Type>
|
||||
iterator find() { return basic_schema::find(typeid(Type)); }
|
||||
|
||||
template<typename Type>
|
||||
iterator find() { return find(typeid(Type)); }
|
||||
iterator find(const std::type_index& ti);
|
||||
iterator find(const std::string& name);
|
||||
const_iterator find() const { return basic_schema::find(typeid(Type)); }
|
||||
|
||||
template<typename Type>
|
||||
const_iterator find() const { return find(typeid(Type)); }
|
||||
const_iterator find(const std::type_index& ti) const;
|
||||
const_iterator find(const std::string& name) const;
|
||||
|
||||
[[nodiscard]] bool contains(const std::type_index &index) const;
|
||||
template < typename Type >
|
||||
[[nodiscard]] bool contains() const {
|
||||
return contains(std::type_index(typeid(Type)));
|
||||
return basic_schema::contains(std::type_index(typeid(Type)));
|
||||
}
|
||||
|
||||
const object::repository &repo() const { return repo_; }
|
||||
@@ -130,14 +128,11 @@ private:
|
||||
[[nodiscard]] static sql::query_context build_add_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||
[[nodiscard]] static sql::query_context build_drop_constraint_context(const object::repository_node& node, const object::restriction& cons, const sql::connection &conn) ;
|
||||
|
||||
void insert_table(const std::type_index& ti, const object::repository_node &node);
|
||||
iterator insert_table(const std::type_index& ti, const object::repository_node &node);
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
friend class schema_observer;
|
||||
|
||||
object::repository repo_;
|
||||
std::unordered_map<std::type_index, schema_node> schema_nodes_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
@@ -151,7 +146,12 @@ utils::result<void, utils::error> schema::drop_table(const sql::connection &conn
|
||||
}
|
||||
template <typename Type>
|
||||
void schema_observer<Type>::on_attach(const object::repository_node &node, const Type &/*prototype*/) const {
|
||||
schema_.insert_table(typeid(Type), node);
|
||||
const auto it = schema_.insert_table(typeid(Type), node);
|
||||
|
||||
if (it->second.node().info().has_primary_key()) {
|
||||
auto producer = std::make_unique<query_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>
|
||||
|
||||
@@ -6,9 +6,7 @@
|
||||
#include "matador/utils/basic_types.hpp"
|
||||
#include "matador/utils/field_attributes.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace matador::query {
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ public:
|
||||
* @param info The database connection info data
|
||||
* @param sql_logger The logging handler
|
||||
*/
|
||||
explicit connection(const connection_info& info, const logger_ptr &sql_logger = null_logger);
|
||||
explicit connection(const connection_info& info, logger_ptr sql_logger = null_logger);
|
||||
explicit connection(const connection_info& info, std::shared_ptr<producer_resolver_factory> resolver_factory, logger_ptr sql_logger = null_logger);
|
||||
/**
|
||||
* @brief Creates a database connection from a connection string.
|
||||
*
|
||||
@@ -33,6 +34,7 @@ public:
|
||||
* @param sql_logger The logging handler
|
||||
*/
|
||||
explicit connection(const std::string &dns, const logger_ptr &sql_logger = null_logger);
|
||||
explicit connection(const std::string &dns, std::shared_ptr<producer_resolver_factory> resolver_factory, const logger_ptr &sql_logger = null_logger);
|
||||
/**
|
||||
* Copies a given connection
|
||||
*
|
||||
@@ -138,6 +140,7 @@ public:
|
||||
[[nodiscard]] std::string str( const query_context& ctx ) const override;
|
||||
|
||||
[[nodiscard]] const class dialect &dialect() const override;
|
||||
[[nodiscard]] std::shared_ptr<producer_resolver_factory> resolver_factory() const override;
|
||||
|
||||
private:
|
||||
[[nodiscard]] utils::result<std::unique_ptr<statement_impl>, utils::error> perform_prepare(const query_context &ctx) const;
|
||||
@@ -149,6 +152,7 @@ private:
|
||||
|
||||
std::unique_ptr<connection_impl> connection_;
|
||||
std::shared_ptr<abstract_sql_logger> logger_ = std::make_shared<null_sql_logger>();
|
||||
std::shared_ptr<producer_resolver_factory> resolver_factory_ = std::make_shared<producer_resolver_factory>();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ private:
|
||||
class connection_pool {
|
||||
public:
|
||||
connection_pool(const std::string &dns, size_t count);
|
||||
connection_pool(const std::string &dns, size_t count, std::function<connection(const connection_info &)> &&connection_creator);
|
||||
|
||||
connection_ptr acquire();
|
||||
connection_ptr try_acquire();
|
||||
@@ -74,6 +75,7 @@ private:
|
||||
connection_map idle_connections_;
|
||||
|
||||
const connection_info info_;
|
||||
std::function<connection(const connection_info &)> connection_creator_;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef EXECUTOR_HPP
|
||||
#define EXECUTOR_HPP
|
||||
|
||||
#include "matador/sql/producer_resolver_factory.hpp"
|
||||
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
@@ -20,6 +22,7 @@ public:
|
||||
[[nodiscard]] virtual utils::result<std::unique_ptr<query_result_impl>, utils::error> fetch(const query_context &ctx) const = 0;
|
||||
[[nodiscard]] virtual utils::result<statement, utils::error> prepare(const query_context &ctx) = 0;
|
||||
[[nodiscard]] virtual std::string str(const query_context &ctx) const = 0;
|
||||
[[nodiscard]] virtual std::shared_ptr<producer_resolver_factory> resolver_factory() const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ public:
|
||||
field(field &&x) noexcept;
|
||||
field& operator=(field &&x) noexcept;
|
||||
|
||||
bool operator==(const field& rhs) const;
|
||||
bool operator!=(const field& rhs) const;
|
||||
|
||||
template<typename Type>
|
||||
field& operator=(Type value) {
|
||||
value_ = std::move(value);
|
||||
|
||||
@@ -7,11 +7,13 @@
|
||||
#include "matador/sql/object_parameter_binder.hpp"
|
||||
|
||||
#include "matador/utils/data_type_traits.hpp"
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
class query_result_impl;
|
||||
class sql_error;
|
||||
|
||||
class statement_impl {
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
[[nodiscard]] std::unique_ptr<utils::attribute_writer> create_binder() const;
|
||||
|
||||
protected:
|
||||
friend class statement;
|
||||
|
||||
std::unique_ptr<statement_impl> statement_;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef MATADOR_IDENTIFIER_READER_HPP
|
||||
#define MATADOR_IDENTIFIER_READER_HPP
|
||||
|
||||
#include "matador/utils/access.hpp"
|
||||
#include "matador/utils/identifier.hpp"
|
||||
#include "matador/utils/primary_key_attribute.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class query_result_reader;
|
||||
}
|
||||
|
||||
namespace matador::sql::internal {
|
||||
class identifier_reader final {
|
||||
public:
|
||||
explicit identifier_reader(query_result_reader &reader);
|
||||
|
||||
template<class Type>
|
||||
utils::identifier read(const Type &obj, const size_t column_index) {
|
||||
identifier_.clear();
|
||||
column_index_ = column_index;
|
||||
access::process(*this, obj);
|
||||
return identifier_;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *id, ValueType &value, const utils::primary_key_attribute& attr = utils::default_pk_attributes) {
|
||||
utils::data_type_traits<ValueType>::read_value(reader_, id, column_index_, value, attr.size());
|
||||
identifier_ = value;
|
||||
}
|
||||
|
||||
static void on_revision(const char * /*id*/, uint64_t &/*rev*/) {}
|
||||
template < class Type >
|
||||
static void on_attribute(const char * /*id*/, Type &/*x*/, const utils::field_attributes &/*attr*/ = utils::null_attributes) {}
|
||||
template < class Pointer >
|
||||
static void on_belongs_to(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template < class Pointer >
|
||||
static void on_has_one(const char * /*id*/, Pointer &/*x*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
query_result_reader &reader_;
|
||||
size_t column_index_{};
|
||||
utils::identifier identifier_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_IDENTIFIER_READER_HPP
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP
|
||||
#define MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class statement;
|
||||
|
||||
class identifier_statement_binder : public utils::identifier_serializer {
|
||||
public:
|
||||
explicit identifier_statement_binder(statement &stmt, size_t index = 0);
|
||||
|
||||
void bind(const utils::identifier &id);
|
||||
|
||||
void serialize(int8_t &, const utils::field_attributes &) override;
|
||||
void serialize(int16_t &, const utils::field_attributes &) override;
|
||||
void serialize(int32_t &, const utils::field_attributes &) override;
|
||||
void serialize(int64_t &, const utils::field_attributes &) override;
|
||||
void serialize(uint8_t &, const utils::field_attributes &) override;
|
||||
void serialize(uint16_t &, const utils::field_attributes &) override;
|
||||
void serialize(uint32_t &, const utils::field_attributes &) override;
|
||||
void serialize(uint64_t &, const utils::field_attributes &) override;
|
||||
void serialize(const char *, const utils::field_attributes &) override;
|
||||
void serialize(std::string &, const utils::field_attributes &) override;
|
||||
void serialize(utils::null_type_t &, const utils::field_attributes &) override;
|
||||
|
||||
private:
|
||||
statement &stmt_;
|
||||
size_t index_{};
|
||||
};
|
||||
|
||||
}
|
||||
#endif //MATADOR_IDENTIFIER_STATEMENT_BINDER_HPP
|
||||
@@ -10,16 +10,17 @@
|
||||
|
||||
#include "matador/sql/interface/query_result_reader.hpp"
|
||||
#include "matador/sql/internal/query_result_pk_resolver.hpp"
|
||||
#include "matador/sql/internal/identifier_reader.hpp"
|
||||
#include "matador/sql/record.hpp"
|
||||
|
||||
#include "matador/object/attribute.hpp"
|
||||
#include "matador/object/object_proxy.hpp"
|
||||
#include "matador/object/object_resolver_factory.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace matador::utils {
|
||||
class value;
|
||||
@@ -73,9 +74,9 @@ private:
|
||||
class query_result_impl {
|
||||
public:
|
||||
query_result_impl(std::unique_ptr<query_result_reader> &&reader,
|
||||
std::vector<object::attribute> &&prototype, size_t column_index = 0);
|
||||
query_result_impl(std::unique_ptr<query_result_reader> &&reader,
|
||||
const std::vector<object::attribute> &prototype, size_t column_index = 0);
|
||||
std::vector<object::attribute> prototype,
|
||||
const std::shared_ptr<object::object_resolver_factory>& resolver_factory,
|
||||
size_t column_index = 0);
|
||||
|
||||
template<typename ValueType>
|
||||
void on_primary_key(const char *id, ValueType &value, const utils::primary_key_attribute& attr = utils::default_pk_attributes) {
|
||||
@@ -122,17 +123,19 @@ public:
|
||||
}
|
||||
|
||||
template<class ContainerType>
|
||||
void on_has_many(const char * /*id*/, ContainerType &cont, const char * /*join_column*/,
|
||||
const utils::foreign_attributes &attr) {
|
||||
void on_has_many(const char * /*id*/, ContainerType &cont, const char * /*join_column*/, const utils::foreign_attributes &attr) {
|
||||
using value_type = typename ContainerType::value_type::value_type;
|
||||
auto resolver = resolver_factory_->resolver<value_type>();
|
||||
|
||||
if (attr.fetch() == utils::fetch_type::Lazy) {
|
||||
// pk_reader_.read(*id, column_index_++);
|
||||
} else {
|
||||
const auto ti = std::type_index(typeid(typename ContainerType::value_type::value_type));
|
||||
auto obj = std::make_unique<typename ContainerType::value_type::value_type>();
|
||||
const auto ti = std::type_index(typeid(value_type));
|
||||
auto obj = std::make_shared<typename ContainerType::value_type::value_type>();
|
||||
type_stack_.push(ti);
|
||||
access::process(*this, *obj);
|
||||
type_stack_.pop();
|
||||
auto ptr = typename ContainerType::value_type(obj.release());
|
||||
auto ptr = typename ContainerType::value_type(std::make_shared<object::object_proxy<value_type>>(resolver, obj));
|
||||
const auto pk = ptr.primary_key();
|
||||
if (ptr.primary_key().is_valid()) {
|
||||
cont.push_back(ptr);
|
||||
@@ -194,16 +197,18 @@ private:
|
||||
|
||||
template<class Pointer>
|
||||
void on_foreign_key(Pointer &x, const utils::foreign_attributes &attr) {
|
||||
if (x.empty()) {
|
||||
x.reset(new typename Pointer::value_type);
|
||||
}
|
||||
const auto resolver = resolver_factory_->resolver<typename Pointer::value_type>();
|
||||
if (attr.fetch() == utils::fetch_type::Lazy) {
|
||||
pk_reader_.read(*x, column_index_++);
|
||||
typename Pointer::value_type obj;
|
||||
auto pk = id_reader_.read(obj, column_index_++);
|
||||
x = Pointer(std::make_shared<object::object_proxy<typename Pointer::value_type>>(resolver, pk));
|
||||
} else {
|
||||
auto obj = std::make_shared<typename Pointer::value_type>();
|
||||
const auto ti = std::type_index(typeid(*x));
|
||||
type_stack_.push(ti);
|
||||
access::process(*this, *x);
|
||||
access::process(*this, *obj);
|
||||
type_stack_.pop();
|
||||
x = Pointer(std::make_shared<object::object_proxy<typename Pointer::value_type>>(resolver, obj));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,6 +216,8 @@ protected:
|
||||
size_t column_index_ = 0;
|
||||
std::vector<object::attribute> prototype_;
|
||||
std::unique_ptr<query_result_reader> reader_;
|
||||
std::shared_ptr<object::object_resolver_factory> resolver_factory_;
|
||||
internal::identifier_reader id_reader_;
|
||||
detail::pk_reader pk_reader_;
|
||||
std::stack<std::type_index> type_stack_;
|
||||
utils::identifier current_pk_{};
|
||||
|
||||
@@ -50,9 +50,9 @@ public:
|
||||
template<class ContainerType>
|
||||
static void on_has_many(const char * /*id*/, ContainerType &, const char *, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char *id, ContainerType &c, const char *join_column, const char *inverse_join_column, const utils::foreign_attributes &/*attr*/) {}
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const char * /*join_column*/, const char * /*inverse_join_column*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
template<class ContainerType>
|
||||
static void on_has_many_to_many(const char *id, ContainerType &c, const utils::foreign_attributes &/*attr*/) {}
|
||||
static void on_has_many_to_many(const char * /*id*/, ContainerType & /*c*/, const utils::foreign_attributes &/*attr*/) {}
|
||||
|
||||
private:
|
||||
template<class Type>
|
||||
@@ -60,8 +60,8 @@ private:
|
||||
if (fetch == utils::fetch_type::Lazy) {
|
||||
++column_index_;
|
||||
} else {
|
||||
const Type obj;
|
||||
type_stack_.push(typeid(Type));
|
||||
const Type obj{};
|
||||
type_stack_.emplace(typeid(Type));
|
||||
access::process(*this, obj);
|
||||
type_stack_.pop();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef MATADOR_RESOLVER_PRODUCER_HPP
|
||||
#define MATADOR_RESOLVER_PRODUCER_HPP
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
class executor;
|
||||
class resolver_producer {
|
||||
public:
|
||||
virtual ~resolver_producer() = default;
|
||||
virtual std::shared_ptr<object::abstract_object_resolver> produce(executor &exec) = 0;
|
||||
|
||||
[[nodiscard]] const std::type_index& type() const;
|
||||
|
||||
protected:
|
||||
explicit resolver_producer(const std::type_index &type);
|
||||
|
||||
private:
|
||||
std::type_index type_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_RESOLVER_PRODUCER_HPP
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef MATADOR_STATEMENT_OBJECT_RESOLVER_HPP
|
||||
#define MATADOR_STATEMENT_OBJECT_RESOLVER_HPP
|
||||
|
||||
#include "matador/object/object_resolver.hpp"
|
||||
|
||||
#include "matador/sql/statement.hpp"
|
||||
#include "matador/sql/internal/identifier_statement_binder.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
template<typename Type>
|
||||
class statement_object_resolver : public object::object_resolver<Type> {
|
||||
public:
|
||||
explicit statement_object_resolver(statement stmt)
|
||||
: statement_(std::move(stmt)) {}
|
||||
|
||||
std::shared_ptr<Type> resolve(const utils::identifier &id) override;
|
||||
protected:
|
||||
statement statement_;
|
||||
std::type_index index{typeid(Type)};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
std::shared_ptr<Type> statement_object_resolver<Type>::resolve(const utils::identifier &id) {
|
||||
identifier_statement_binder binder(statement_);
|
||||
binder.bind(id);
|
||||
|
||||
auto result = statement_.template fetch_one<Type>();
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_shared<Type>(*result);
|
||||
}
|
||||
|
||||
}
|
||||
#endif //MATADOR_STATEMENT_OBJECT_RESOLVER_HPP
|
||||
@@ -0,0 +1,21 @@
|
||||
#ifndef MATADOR_RESOLVER_FACTORY_HPP
|
||||
#define MATADOR_RESOLVER_FACTORY_HPP
|
||||
|
||||
#include "matador/object/object_resolver_factory.hpp"
|
||||
|
||||
#include "matador/sql/internal/resolver_producer.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class executor;
|
||||
|
||||
class producer_resolver_factory : public object::object_resolver_factory {
|
||||
public:
|
||||
|
||||
std::shared_ptr<object::abstract_object_resolver> acquire_resolver(const std::type_index &type) override;
|
||||
void register_resolver(std::shared_ptr<object::abstract_object_resolver> &&resolver) override;
|
||||
|
||||
private:
|
||||
std::unordered_map<std::type_index, std::shared_ptr<object::abstract_object_resolver>> resolvers_;
|
||||
};
|
||||
}
|
||||
#endif //MATADOR_RESOLVER_FACTORY_HPP
|
||||
@@ -2,31 +2,29 @@
|
||||
#define QUERY_QUERY_DATA_HPP
|
||||
|
||||
#include "matador/object/attribute.hpp"
|
||||
#include "matador/object/object_resolver_factory.hpp"
|
||||
|
||||
#include "matador/utils/types.hpp"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
enum class sql_command {
|
||||
SQL_UNKNOWN,
|
||||
SQL_CREATE,
|
||||
SQL_CREATE_TABLE,
|
||||
SQL_CREATE_SCHEMA,
|
||||
SQL_CREATE_DATABASE,
|
||||
SQL_UPDATE,
|
||||
SQL_INSERT,
|
||||
SQL_DELETE,
|
||||
SQL_SELECT,
|
||||
SQL_DROP,
|
||||
SQL_DROP_TABLE,
|
||||
SQL_DROP_SCHEMA,
|
||||
SQL_DROP_DATABASE,
|
||||
SQL_ALTER,
|
||||
SQL_ALTER_TABLE,
|
||||
SQL_ALTER_SCHEMA
|
||||
};
|
||||
SQL_UNKNOWN,
|
||||
SQL_CREATE,
|
||||
SQL_CREATE_TABLE,
|
||||
SQL_CREATE_SCHEMA,
|
||||
SQL_CREATE_DATABASE,
|
||||
SQL_UPDATE,
|
||||
SQL_INSERT,
|
||||
SQL_DELETE,
|
||||
SQL_SELECT,
|
||||
SQL_DROP,
|
||||
SQL_DROP_TABLE,
|
||||
SQL_DROP_SCHEMA,
|
||||
SQL_DROP_DATABASE,
|
||||
SQL_ALTER,
|
||||
SQL_ALTER_TABLE,
|
||||
SQL_ALTER_SCHEMA
|
||||
};
|
||||
|
||||
struct sql_command_info {
|
||||
std::string sql;
|
||||
@@ -40,14 +38,10 @@ struct query_context {
|
||||
std::string schema_name{};
|
||||
std::string table_name{};
|
||||
std::vector<object::attribute> prototype{};
|
||||
// std::vector<std::string> result_vars{};
|
||||
std::vector<std::string> bind_vars{};
|
||||
std::vector<utils::database_type> bind_types{};
|
||||
|
||||
// std::unordered_map<std::string, std::string> column_aliases{};
|
||||
// std::unordered_map<std::string, std::string> table_aliases{};
|
||||
std::shared_ptr<object::object_resolver_factory> resolver_factory{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //QUERY_QUERY_DATA_HPP
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
#ifndef MATADOR_QUERY_RECORD_RESULT_HPP
|
||||
#define MATADOR_QUERY_RECORD_RESULT_HPP
|
||||
|
||||
#include "matador/sql/query_result.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
|
||||
template <>
|
||||
class query_result<record>;
|
||||
|
||||
template <>
|
||||
class query_result_iterator<record> {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = record;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using self = query_result_iterator; /**< Shortcut for this class. */
|
||||
using pointer = value_type*; /**< Shortcut for the pointer type. */
|
||||
using reference = value_type&; /**< Shortcut for the reference type */
|
||||
|
||||
public:
|
||||
query_result_iterator() = default;
|
||||
explicit query_result_iterator(query_result<record> *res)
|
||||
: result_(res)
|
||||
{}
|
||||
query_result_iterator(query_result<record> *res, record rec)
|
||||
: record_(std::move(rec))
|
||||
, result_(res)
|
||||
{}
|
||||
query_result_iterator(query_result_iterator&& x) noexcept
|
||||
: record_(std::move(x.record_))
|
||||
, result_(x.result_)
|
||||
{}
|
||||
query_result_iterator& operator=(query_result_iterator&& x) noexcept {
|
||||
result_ = x.result_;
|
||||
record_ = std::move(x.record_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
~query_result_iterator() = default;
|
||||
|
||||
bool operator==(const query_result_iterator& rhs) const {
|
||||
return record_ == rhs.record_;
|
||||
}
|
||||
|
||||
bool operator!=(const query_result_iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
self& operator++();
|
||||
self operator++(int);
|
||||
|
||||
pointer operator->() { return &record_; }
|
||||
reference operator*() { return record_; }
|
||||
record release() { return std::move(record_); }
|
||||
|
||||
private:
|
||||
record record_;
|
||||
query_result<record> *result_{nullptr};
|
||||
};
|
||||
|
||||
template<>
|
||||
class query_result<record> final {
|
||||
public:
|
||||
using iterator = query_result_iterator<record>;
|
||||
|
||||
query_result(std::unique_ptr<query_result_impl> &&impl, const std::vector<object::attribute> &prototype)
|
||||
: impl_(std::move(impl))
|
||||
, prototype_(prototype) {}
|
||||
|
||||
iterator begin() { return std::move(++iterator(this)); }
|
||||
iterator end() { return {}; }
|
||||
|
||||
private:
|
||||
friend class query_result_iterator<record>;
|
||||
|
||||
[[nodiscard]] record create() const;
|
||||
void bind(const record& obj) const;
|
||||
bool fetch(record& obj) const;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<query_result_impl> impl_;
|
||||
std::vector<object::attribute> prototype_;
|
||||
};
|
||||
|
||||
inline record query_result<record>::create() const {
|
||||
record rec;
|
||||
int index{0};
|
||||
for (const auto &col: prototype_) {
|
||||
rec.append({
|
||||
col.name(),
|
||||
col.type(),
|
||||
col.attributes().options(),
|
||||
col.attributes().size(),
|
||||
index++
|
||||
});
|
||||
}
|
||||
return rec;
|
||||
|
||||
}
|
||||
|
||||
inline void query_result<record>::bind(const record &obj) const {
|
||||
impl_->bind(obj);
|
||||
}
|
||||
|
||||
inline bool query_result<record>::fetch(record &obj) const {
|
||||
return impl_->fetch(obj);
|
||||
}
|
||||
|
||||
inline query_result_iterator<record>::self & query_result_iterator<record>::operator++() {
|
||||
record_ = result_->create();
|
||||
result_->bind(record_);
|
||||
if (!result_->fetch(record_)) {
|
||||
record_.clear();
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline query_result_iterator<record>::self query_result_iterator<record>::operator++(int) {
|
||||
self tmp(result_, record_);
|
||||
|
||||
record_ = result_->create();
|
||||
result_->bind(record_);
|
||||
if (!result_->fetch(record_)) {
|
||||
record_.clear();
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
#endif //MATADOR_QUERY_RECORD_RESULT_HPP
|
||||
@@ -2,22 +2,24 @@
|
||||
#define QUERY_QUERY_RESULT_HPP
|
||||
|
||||
#include "matador/object/attribute.hpp"
|
||||
#include "matador/object/object_ptr.hpp"
|
||||
|
||||
#include "matador/sql/internal/query_result_impl.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
namespace matador::sql {
|
||||
#include "matador/object/object.hpp"
|
||||
|
||||
namespace matador::sql {
|
||||
class record;
|
||||
class query_result_impl;
|
||||
|
||||
template < typename Type >
|
||||
class query_result;
|
||||
|
||||
template < typename Type >
|
||||
class query_result_iterator
|
||||
{
|
||||
class query_result_iterator {
|
||||
public:
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using value_type = Type;
|
||||
@@ -31,7 +33,7 @@ public:
|
||||
explicit query_result_iterator(query_result<Type> *res)
|
||||
: result_(res)
|
||||
{}
|
||||
query_result_iterator(query_result<Type> *res, std::unique_ptr<Type> obj)
|
||||
query_result_iterator(query_result<Type> *res, object::object_ptr<Type> obj)
|
||||
: obj_(std::move(obj))
|
||||
, result_(res)
|
||||
{}
|
||||
@@ -48,19 +50,20 @@ public:
|
||||
|
||||
~query_result_iterator() = default;
|
||||
|
||||
bool operator==(const query_result_iterator& rhs) {
|
||||
bool operator==(const query_result_iterator& rhs) const {
|
||||
return obj_ == rhs.obj_;
|
||||
}
|
||||
|
||||
bool operator!=(const query_result_iterator& rhs) {
|
||||
return obj_ != rhs.obj_;
|
||||
bool operator!=(const query_result_iterator& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
self& operator++() {
|
||||
obj_.reset(result_->create());
|
||||
result_->bind(*obj_);
|
||||
if (!result_->fetch(*obj_)) {
|
||||
obj_.reset();
|
||||
auto obj = result_->create();
|
||||
result_->bind(*obj);
|
||||
obj_.reset();
|
||||
if (result_->fetch(*obj)) {
|
||||
obj_ = object::object_ptr<Type>(std::make_shared<object::object_proxy<Type>>(result_->resolver_, std::move(obj)));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -69,10 +72,11 @@ public:
|
||||
self operator++(int) {
|
||||
const self tmp(result_, obj_);
|
||||
|
||||
obj_.reset(result_->create());
|
||||
result_->bind(*obj_);
|
||||
if (!result_->fetch(*obj_)) {
|
||||
obj_.reset();
|
||||
auto obj = result_->create();
|
||||
result_->bind(*obj);
|
||||
obj_.reset();
|
||||
if (result_->fetch(*obj)) {
|
||||
obj_ = object::object_ptr<Type>(std::make_shared<object::object_proxy<Type>>(result_->resolver_, std::move(obj)));
|
||||
}
|
||||
|
||||
return tmp;
|
||||
@@ -82,50 +86,37 @@ public:
|
||||
return obj_.get();
|
||||
}
|
||||
|
||||
reference operator*() {
|
||||
return *obj_;
|
||||
object::object_ptr<Type> operator*() {
|
||||
return obj_;
|
||||
}
|
||||
|
||||
pointer get() {
|
||||
return obj_.get();
|
||||
}
|
||||
|
||||
pointer release() {
|
||||
return obj_.release();
|
||||
object::object_ptr<Type> optr() {
|
||||
return obj_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<Type> obj_;
|
||||
object::object_ptr<Type> obj_;
|
||||
query_result<Type> *result_{nullptr};
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template < typename Type >
|
||||
Type* create_prototype(const std::vector<object::attribute> &/*prototype*/) {
|
||||
return new Type{};
|
||||
std::shared_ptr<Type> create_prototype(const std::vector<object::attribute> &/*prototype*/) {
|
||||
return std::make_shared<Type>();
|
||||
}
|
||||
|
||||
template <>
|
||||
record* create_prototype<record>(const std::vector<object::attribute> &prototype);
|
||||
|
||||
record create_prototype(const std::vector<object::attribute> &prototype);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
class query_result final {
|
||||
public:
|
||||
using iterator = query_result_iterator<Type>;
|
||||
using creator_func = std::function<Type*()>;
|
||||
using creator_func = std::function<std::shared_ptr<Type>()>;
|
||||
|
||||
public:
|
||||
// explicit query_result(std::unique_ptr<query_result_impl> &&impl)
|
||||
// : impl_(std::move(impl))
|
||||
// , creator_([this] {
|
||||
// return detail::create_prototype<Type>(impl_->prototype());
|
||||
// }) {}
|
||||
|
||||
query_result(std::unique_ptr<query_result_impl> &&impl, creator_func&& creator)
|
||||
query_result(std::unique_ptr<query_result_impl> &&impl, const std::shared_ptr<object::object_resolver<Type>> &resolver, creator_func&& creator)
|
||||
: impl_(std::move(impl))
|
||||
, resolver_(resolver)
|
||||
, creator_(std::move(creator)) {}
|
||||
|
||||
iterator begin() { return std::move(++iterator(this)); }
|
||||
@@ -134,17 +125,18 @@ public:
|
||||
private:
|
||||
friend class query_result_iterator<Type>;
|
||||
|
||||
Type* create();
|
||||
std::shared_ptr<Type> create();
|
||||
void bind(const Type& obj);
|
||||
bool fetch(Type& obj);
|
||||
|
||||
protected:
|
||||
std::unique_ptr<query_result_impl> impl_;
|
||||
std::shared_ptr<object::object_resolver<Type>> resolver_;
|
||||
creator_func creator_;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
Type *query_result<Type>::create() {
|
||||
std::shared_ptr<Type> query_result<Type>::create() {
|
||||
return creator_();
|
||||
}
|
||||
|
||||
@@ -157,6 +149,6 @@ template<typename Type>
|
||||
bool query_result<Type>::fetch(Type &obj) {
|
||||
return impl_->fetch(obj);
|
||||
}
|
||||
|
||||
} // namespace matador::sql
|
||||
}
|
||||
// namespace matador::sql
|
||||
#endif //QUERY_QUERY_RESULT_HPP
|
||||
|
||||
@@ -28,6 +28,9 @@ public:
|
||||
record& operator=(record&&) noexcept = default;
|
||||
~record() = default;
|
||||
|
||||
bool operator==(const record& rhs) const;
|
||||
bool operator!=(const record& rhs) const;
|
||||
|
||||
template<class Operator>
|
||||
void process(Operator &op) {
|
||||
for(auto &f : fields_) {
|
||||
|
||||
@@ -2,20 +2,20 @@
|
||||
#define QUERY_STATEMENT_HPP
|
||||
|
||||
#include "matador/sql/abstract_sql_logger.hpp"
|
||||
#include "matador/sql/error_code.hpp"
|
||||
#include "matador/sql/query_result.hpp"
|
||||
|
||||
#include "matador/sql/interface/statement_proxy.hpp"
|
||||
|
||||
#include "matador/object/basic_repository.hpp"
|
||||
|
||||
#include "matador/utils/error.hpp"
|
||||
#include "matador/utils/result.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
|
||||
namespace matador::sql {
|
||||
namespace detail {
|
||||
template<class Type>
|
||||
class identifier_binder;
|
||||
}
|
||||
|
||||
class statement_impl;
|
||||
class statement_proxy;
|
||||
@@ -104,7 +104,10 @@ public:
|
||||
* @return The query result set
|
||||
*/
|
||||
template<class Type>
|
||||
utils::result<std::unique_ptr<Type>, utils::error> fetch_one();
|
||||
utils::result<object::object_ptr<Type>, utils::error> fetch_one();
|
||||
|
||||
template<class Type>
|
||||
utils::result<std::shared_ptr<Type>, utils::error> fetch_one_raw();
|
||||
/**
|
||||
* Fetches the first result of a prepared statement.
|
||||
* The type is a record representing an unknown variable type.
|
||||
@@ -140,10 +143,6 @@ public:
|
||||
|
||||
[[nodiscard]] utils::result<std::unique_ptr<query_result_impl>, utils::error> fetch_internal() const;
|
||||
|
||||
private:
|
||||
template<class Type>
|
||||
friend class detail::identifier_binder;
|
||||
|
||||
private:
|
||||
std::shared_ptr<statement_proxy> statement_proxy_;
|
||||
std::unique_ptr<utils::attribute_writer> bindings_;
|
||||
@@ -165,31 +164,51 @@ statement &statement::bind(const Type &obj) {
|
||||
template<class Type>
|
||||
utils::result<query_result<Type>, utils::error> statement::fetch() {
|
||||
std::cout << statement_proxy_->sql() << std::endl;
|
||||
return statement_proxy_->fetch(*bindings_).and_then([](std::unique_ptr<query_result_impl> &&value) {
|
||||
return statement_proxy_->fetch(*bindings_).and_then([this](std::unique_ptr<query_result_impl> &&value) {
|
||||
auto resolver = statement_proxy_->statement_->query_.resolver_factory->resolver<Type>();
|
||||
const auto prototype = value->prototype();
|
||||
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), [prototype] {
|
||||
return utils::ok(query_result<Type>(std::forward<decltype(value)>(value), resolver, [prototype] {
|
||||
return detail::create_prototype<Type>(prototype);
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
utils::result<std::unique_ptr<Type>, utils::error> statement::fetch_one() {
|
||||
utils::result<object::object_ptr<Type>, utils::error> statement::fetch_one() {
|
||||
std::cout << statement_proxy_->sql() << std::endl;
|
||||
auto result = statement_proxy_->fetch(*bindings_);
|
||||
if (!result.is_ok()) {
|
||||
return utils::failure(result.err());
|
||||
}
|
||||
auto resolver = statement_proxy_->statement_->query_.resolver_factory->resolver<Type>();
|
||||
const auto prototype = result.value()->prototype();
|
||||
auto records = query_result<Type>(result.release(), [prototype] {
|
||||
auto records = query_result<Type>(result.release(), resolver, [prototype] {
|
||||
return detail::create_prototype<Type>(prototype);
|
||||
});
|
||||
auto first = records.begin();
|
||||
if (first == records.end()) {
|
||||
return utils::ok(std::unique_ptr<Type>{nullptr});
|
||||
return utils::failure(utils::error{
|
||||
error_code::FETCH_FAILED,
|
||||
"Failed to find entity."
|
||||
});
|
||||
}
|
||||
|
||||
return utils::ok(std::unique_ptr<Type>{first.release()});
|
||||
return utils::ok(first.optr());
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
utils::result<std::shared_ptr<Type>, utils::error> statement::fetch_one_raw() {
|
||||
auto result = statement_proxy_->fetch(*bindings_);
|
||||
if (!result.is_ok()) {
|
||||
return utils::failure(result.err());
|
||||
}
|
||||
|
||||
auto obj = std::make_shared<Type>();
|
||||
(*result)->bind(*obj);
|
||||
if (!(*result)->fetch(*obj)) {
|
||||
return utils::ok(std::shared_ptr<Type>{});
|
||||
}
|
||||
return utils::ok(obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ private:
|
||||
std::mutex mutex_;
|
||||
|
||||
connection_pool &pool_;
|
||||
const sql::dialect &dialect_;
|
||||
const dialect &dialect_;
|
||||
|
||||
utils::message_bus &bus_;
|
||||
};
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
#include <typeindex>
|
||||
|
||||
namespace matador::utils {
|
||||
|
||||
class identifier_serializer
|
||||
{
|
||||
class identifier_serializer {
|
||||
public:
|
||||
virtual ~identifier_serializer() = default;
|
||||
|
||||
@@ -25,7 +23,7 @@ public:
|
||||
virtual void serialize(uint16_t &, const field_attributes &) = 0;
|
||||
virtual void serialize(uint32_t &, const field_attributes &) = 0;
|
||||
virtual void serialize(uint64_t &, const field_attributes &) = 0;
|
||||
virtual void serialize(const char*, const field_attributes &) = 0;
|
||||
virtual void serialize(const char *, const field_attributes &) = 0;
|
||||
virtual void serialize(std::string &, const field_attributes &) = 0;
|
||||
virtual void serialize(null_type_t &, const field_attributes &) = 0;
|
||||
};
|
||||
@@ -34,13 +32,13 @@ template<typename Type, class Enabled = void>
|
||||
struct identifier_type_traits;
|
||||
|
||||
template<typename Type>
|
||||
struct identifier_type_traits<Type, std::enable_if_t<std::is_integral_v<Type>>> {
|
||||
struct identifier_type_traits<Type, std::enable_if_t<std::is_integral_v<Type> > > {
|
||||
static bool is_valid(Type value) { return value > 0; }
|
||||
static std::string to_string(const Type value) { return std::to_string(value); }
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
struct identifier_type_traits<Type, std::enable_if_t<std::is_same_v<Type, std::string>>> {
|
||||
struct identifier_type_traits<Type, std::enable_if_t<std::is_same_v<Type, std::string> > > {
|
||||
static bool is_valid(const Type &value) { return !value.empty(); }
|
||||
static std::string to_string(const Type &value) { return value; }
|
||||
};
|
||||
@@ -52,27 +50,23 @@ struct identifier_type_traits<null_type_t, void> {
|
||||
};
|
||||
|
||||
template<>
|
||||
struct identifier_type_traits<const char*, void> {
|
||||
struct identifier_type_traits<const char *, void> {
|
||||
static bool is_valid(const char *value);
|
||||
static std::string to_string(const char *value);
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<typename Type>
|
||||
size_t hash(const Type &value) {
|
||||
return std::hash<Type>()(value);
|
||||
}
|
||||
|
||||
size_t hash(const char *value);
|
||||
|
||||
}
|
||||
|
||||
class identifier
|
||||
{
|
||||
class identifier {
|
||||
private:
|
||||
struct base
|
||||
{
|
||||
struct base {
|
||||
base(const std::type_index &ti, basic_type type);
|
||||
base(const base &x) = delete;
|
||||
base &operator=(const base &x) = delete;
|
||||
@@ -93,25 +87,25 @@ private:
|
||||
};
|
||||
|
||||
template<class IdType>
|
||||
struct pk final : base
|
||||
{
|
||||
struct pk final : base {
|
||||
using self = pk;
|
||||
|
||||
explicit pk(const IdType &id)
|
||||
: base(std::type_index(typeid(IdType)), data_type_traits<IdType>::type(1))
|
||||
, id_(id)
|
||||
, size_(sizeof(IdType)) {}
|
||||
, id_(id)
|
||||
, size_(sizeof(IdType)) {
|
||||
}
|
||||
|
||||
[[nodiscard]] base *copy() const override {
|
||||
return new self(id_);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool equal_to(const base &x) const override {
|
||||
return static_cast<const pk&>(x).id_ == id_;
|
||||
return static_cast<const pk &>(x).id_ == id_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool less(const base &x) const override {
|
||||
return id_ < static_cast<const pk&>(x).id_;
|
||||
return id_ < static_cast<const pk &>(x).id_;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool is_valid() const override {
|
||||
@@ -134,8 +128,7 @@ private:
|
||||
size_t size_{};
|
||||
};
|
||||
|
||||
struct null_pk final : base
|
||||
{
|
||||
struct null_pk final : base {
|
||||
null_pk();
|
||||
[[nodiscard]] base *copy() const override;
|
||||
[[nodiscard]] bool equal_to(const base &x) const override;
|
||||
@@ -149,26 +142,29 @@ private:
|
||||
|
||||
public:
|
||||
identifier();
|
||||
|
||||
template<typename Type>
|
||||
explicit identifier(const Type &id)
|
||||
: id_(std::make_shared<pk<Type>>(id)) {}
|
||||
: id_(std::make_shared<pk<Type> >(id)) {
|
||||
}
|
||||
|
||||
explicit identifier(const char *id)
|
||||
: id_(std::make_shared<pk<const char*>>(id)) {}
|
||||
: id_(std::make_shared<pk<const char *> >(id)) {
|
||||
}
|
||||
|
||||
identifier(const identifier &x);
|
||||
identifier &operator=(const identifier &x);
|
||||
identifier(identifier &&x) noexcept ;
|
||||
identifier(identifier &&x) noexcept;
|
||||
identifier &operator=(identifier &&x) noexcept;
|
||||
|
||||
template<typename Type>
|
||||
identifier& operator=(const Type &value)
|
||||
{
|
||||
id_ = std::make_shared<pk<Type>>(value);
|
||||
identifier &operator=(const Type &value) {
|
||||
id_ = std::make_shared<pk<Type> >(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
identifier& operator=(const char *value)
|
||||
{
|
||||
id_ = std::make_shared<pk<const char*>>(value);
|
||||
identifier &operator=(const char *value) {
|
||||
id_ = std::make_shared<pk<const char *> >(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -208,7 +204,7 @@ public:
|
||||
friend std::ostream &operator<<(std::ostream &out, const identifier &id);
|
||||
|
||||
private:
|
||||
explicit identifier(const std::shared_ptr<base>& id);
|
||||
explicit identifier(const std::shared_ptr<base> &id);
|
||||
|
||||
private:
|
||||
std::shared_ptr<base> id_;
|
||||
@@ -217,10 +213,10 @@ private:
|
||||
static identifier null_identifier{};
|
||||
|
||||
/// @cond MATADOR_DEV
|
||||
struct id_pk_hash
|
||||
{
|
||||
struct id_pk_hash {
|
||||
size_t operator()(const identifier &id) const;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP
|
||||
#define MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP
|
||||
|
||||
#include "matador/utils/identifier.hpp"
|
||||
#include "matador/utils/value.hpp"
|
||||
|
||||
namespace matador::utils {
|
||||
class identifier_to_value_converter final : public identifier_serializer {
|
||||
public:
|
||||
[[nodiscard]] value convert(const identifier &id);
|
||||
|
||||
void serialize(int8_t &, const field_attributes &) override;
|
||||
void serialize(int16_t &, const field_attributes &) override;
|
||||
void serialize(int32_t &, const field_attributes &) override;
|
||||
void serialize(int64_t &, const field_attributes &) override;
|
||||
void serialize(uint8_t &, const field_attributes &) override;
|
||||
void serialize(uint16_t &, const field_attributes &) override;
|
||||
void serialize(uint32_t &, const field_attributes &) override;
|
||||
void serialize(uint64_t &, const field_attributes &) override;
|
||||
void serialize(const char *, const field_attributes &) override;
|
||||
void serialize(std::string &, const field_attributes &) override;
|
||||
void serialize(null_type_t &, const field_attributes &) override;
|
||||
|
||||
private:
|
||||
value value_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif //MATADOR_IDENTIFIER_TO_VALUE_CONVERTER_HPP
|
||||
@@ -44,6 +44,9 @@ public:
|
||||
value(value &&x) noexcept;
|
||||
value& operator=(value &&x) noexcept;
|
||||
|
||||
bool operator==(const value& rhs) const;
|
||||
bool operator!=(const value& rhs) const;
|
||||
|
||||
template<class Type>
|
||||
std::optional<Type> as() const {
|
||||
if (std::holds_alternative<Type>(value_)) {
|
||||
|
||||
Reference in New Issue
Block a user