* implemented simple read methods of mariadb drivermaster
@@ -73,18 +73,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
public: | |||
template<typename T> | |||
inline decltype(auto) get() const | |||
{ | |||
if (!data) | |||
throw misc::hibernate_exception("no data assigned!"); | |||
auto type_id = misc::get_type_id(hana::type_c<mp::decay_t<T>>); | |||
if (type_id != data_id) | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
<< "invalid type! expected " << data_id << ", got " << type_id).str()); | |||
} | |||
return *static_cast<T*>(data); | |||
} | |||
inline decltype(auto) get(const table_t* table = nullptr) const; | |||
template<typename T_data> | |||
inline data_context( | |||
@@ -141,5 +130,46 @@ beg_namespace_cpphibernate_driver_mariadb | |||
{ } | |||
}; | |||
/* read_context */ | |||
struct read_context | |||
: public filter_context | |||
{ | |||
bool is_dynamic; | |||
size_t base_dataset_id; | |||
std::string where; | |||
std::string limit; | |||
std::string order_by; | |||
template<typename T_data> | |||
inline read_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter) | |||
: filter_context (p_data, p_schema, p_connection, p_filter) | |||
, is_dynamic (false) | |||
, base_dataset_id (0) | |||
{ } | |||
virtual ~read_context() = default; | |||
template<typename T_dataset> | |||
inline T_dataset& emplace(const table_t* table = nullptr) const; | |||
void emplace() const | |||
{ emplace_intern(nullptr); } | |||
void finish() const | |||
{ finish_intern(); } | |||
private: | |||
virtual void* emplace_intern(void* data) const | |||
{ throw misc::hibernate_exception("read_context::emplace_intern is not implemented!"); } | |||
virtual void finish_intern() const | |||
{ throw misc::hibernate_exception("read_context::finish_intern is not implemented!"); } | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -0,0 +1,67 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/helper/context.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* data_context */ | |||
template<typename T> | |||
inline decltype(auto) data_context | |||
::get(const table_t* table) const | |||
{ | |||
if (!data) | |||
throw misc::hibernate_exception("no data assigned!"); | |||
auto type_id = misc::get_type_id(hana::type_c<mp::decay_t<T>>); | |||
if (type_id != data_id) | |||
{ | |||
if (!table) | |||
table = &schema.table(type_id); | |||
while(table && table->dataset_id != type_id) | |||
table = table->base_table; | |||
if (!table) | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
<< "invalid type! expected " << data_id << ", got " << type_id).str()); | |||
} | |||
} | |||
return *static_cast<T*>(data); | |||
} | |||
/* read_context */ | |||
template<typename T_dataset> | |||
T_dataset& read_context | |||
::emplace(const table_t* table) const | |||
{ | |||
if (!is_dynamic || base_dataset_id == 0) | |||
throw misc::hibernate_exception("dynamic creation is deactivated for this context!"); | |||
// check table | |||
auto dataset_id = misc::get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||
if (!table) | |||
table = &schema.table(dataset_id); | |||
else if (table->dataset_id != dataset_id) | |||
throw misc::hibernate_exception("dataset id of table and dataset to insert defer!"); | |||
// check base | |||
auto tbl = table; | |||
while (tbl && tbl->dataset_id != base_dataset_id) | |||
tbl = tbl->base_table; | |||
if (!tbl) | |||
throw misc::hibernate_exception(utl::type_helper<T_dataset>::name() + " is not a derived type of table " + table->table_name); | |||
// create dataset | |||
auto ptr = std::make_unique<T_dataset>(); | |||
auto ret = emplace_intern(ptr.get()); | |||
if (!ret) | |||
ret = ptr.release(); | |||
return *static_cast<T_dataset*>(ret); | |||
} | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -2,4 +2,8 @@ | |||
#include <cpphibernate/driver/mariadb/impl/create_update.h> | |||
#include <cpphibernate/driver/mariadb/impl/limit.h> | |||
#include <cpphibernate/driver/mariadb/impl/where.h> | |||
#include <cpphibernate/driver/mariadb/impl/order_by.h> | |||
#include <cpphibernate/driver/mariadb/impl/read.h> | |||
#include <cpphibernate/driver/mariadb/impl/where.h> | |||
#include <cpphibernate/driver/mariadb/helper/context.inl> |
@@ -22,17 +22,17 @@ beg_namespace_cpphibernate_driver_mariadb | |||
hana::for_each(modifier, [&limit, &offset](auto& modifier){ | |||
using modifier_type = mp::decay_t<decltype(modifier)>; | |||
using is_limit_type = modifier::is_limit_modifier<modifier_type>; | |||
using is_offset_type = modifier::is_offset_modifier<modifier_type>; | |||
using is_offset_type = modifier::is_offset<modifier_type>; | |||
hana::eval_if( | |||
is_limit_type { }, | |||
[&limit, &modifier](auto _){ | |||
limit = hana::value(_(modifier).value); | |||
limit = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||
}, | |||
[&offset, &modifier](){ | |||
hana::eval_if( | |||
is_offset_type { }, | |||
[&offset, &modifier](auto _){ | |||
offset = hana::value(_(modifier).value); | |||
offset = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||
}, | |||
[]{ | |||
/* no-op */ | |||
@@ -48,7 +48,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
std::ostringstream ss; | |||
ss << "LIMIT " << limit; | |||
if (offset >= 0) | |||
ss << " OFFSET" << offset; | |||
ss << " OFFSET " << offset; | |||
statement.assign(ss.str()); | |||
} | |||
} | |||
@@ -24,7 +24,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
mp::list<T_modifier>, | |||
mp::enable_if_c< | |||
modifier::is_limit_modifier<mp::decay_t<T_modifier>>::value | |||
|| modifier::is_offset_modifier<mp::decay_t<T_modifier>>::value>> | |||
|| modifier::is_offset<mp::decay_t<T_modifier>>::value>> | |||
{ | |||
static constexpr decltype(auto) apply(T_modifier&&) | |||
{ return T_modifier { }; } | |||
@@ -0,0 +1,83 @@ | |||
#pragma once | |||
#include <sstream> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* order_by_builder */ | |||
template<typename T_modifiers> | |||
struct order_by_builder | |||
{ | |||
private: | |||
struct build_t | |||
{ | |||
const schema_t& schema; | |||
const T_modifiers& modifiers; | |||
std::ostringstream os; | |||
inline build_t(const schema_t& p_schema, const T_modifiers& p_modifiers) | |||
: schema (p_schema) | |||
, modifiers (p_modifiers) | |||
{ } | |||
inline void build(::cppmariadb::statement& statement) | |||
{ | |||
size_t index = 0; | |||
hana::for_each(modifiers, [&](auto& modifier){ | |||
using modifier_type = mp::decay_t<decltype(modifier)>; | |||
using is_order_by_type = modifier::is_order_by<modifier_type>; | |||
hana::eval_if( | |||
is_order_by_type { }, | |||
[&](auto _){ | |||
hana::for_each(_(modifier).fields, [&](auto& f){ | |||
using field_type = mp::decay_t<decltype(f)>; | |||
using is_ascencing_type = modifier::is_ascending<field_type>; | |||
if (index++ == 0) os << "ORDER_BY "; | |||
else os << ", "; | |||
auto& field = schema.field(misc::get_type_id(f.wrapped_field)); | |||
os << "`" | |||
<< field.table_name | |||
<< "`.`" | |||
<< field.field_name | |||
<< "` " | |||
<< (is_ascencing_type::value | |||
? "ASC" | |||
: "DESC"); | |||
}); | |||
}, | |||
[]{ }); | |||
}); | |||
statement.assign(os.str()); | |||
} | |||
}; | |||
private: | |||
const schema_t* _schema { nullptr }; | |||
::cppmariadb::statement _statement; | |||
public: | |||
inline ::cppmariadb::statement& assign(const schema_t& schema, const T_modifiers& modifiers) | |||
{ | |||
if (_schema != &schema) | |||
{ | |||
build_t(schema, modifiers).build(_statement); | |||
_schema = &schema; | |||
} | |||
return _statement; | |||
} | |||
}; | |||
template<typename T_modifiers> | |||
inline decltype(auto) build_order_by(const schema_t& schema, const T_modifiers& modifiers) | |||
{ | |||
static order_by_builder<T_modifiers> builder; | |||
return builder.assign(schema, modifiers); | |||
} | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -0,0 +1,93 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* read_impl_t */ | |||
template<typename T_dataset, typename = void> | |||
struct read_impl_t | |||
{ | |||
using dataset_type = T_dataset; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
mutable size_t count; | |||
template<typename T_read_context> | |||
context_impl(T_read_context&& p_read_context) | |||
: read_context (std::forward<T_read_context>(p_read_context)) | |||
, count (0) | |||
{ } | |||
private: | |||
virtual void* emplace_intern(void* data) const override | |||
{ | |||
if (data) | |||
throw misc::hibernate_exception("This context has a pre assigned dataset and can therefor not work in dynamic mode!"); | |||
++count; | |||
if (count > 1) | |||
throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||
return nullptr; | |||
} | |||
virtual void finish_intern() const override | |||
{ | |||
if (count < 1) | |||
throw misc::hibernate_exception("Expected exactly one dataset, but received none!"); | |||
} | |||
}; | |||
static inline void apply(const read_context& context) | |||
{ | |||
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
auto& connection = context.connection; | |||
auto& schema = context.schema; | |||
auto& table = schema.table(dataset_id); | |||
transaction_lock trans(connection); | |||
table.read(context_impl(context)); | |||
trans.commit(); | |||
} | |||
}; | |||
/* read_impl_t - nullable */ | |||
template<typename T_dataset> | |||
struct read_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_nullable<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||
static inline void apply(const read_context& context) | |||
{ | |||
} | |||
}; | |||
/* read_impl_t - container */ | |||
template<typename T_dataset> | |||
struct read_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_container<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
static inline void apply(const read_context& context) | |||
{ | |||
} | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -87,7 +87,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
size_t index = 0; | |||
hana::for_each(modifiers, [&](auto& modifier){ | |||
using modifier_type = mp::decay_t<decltype(modifier)>; | |||
using is_where_type = modifier::is_where_modifier<modifier_type>; | |||
using is_where_type = modifier::is_where<modifier_type>; | |||
hana::eval_if( | |||
is_where_type { }, | |||
[&](auto _){ | |||
@@ -144,7 +144,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
hana::for_each(modifiers, [&](auto& modifier){ | |||
using modifier_type = mp::decay_t<decltype(modifier)>; | |||
using is_where_type = modifier::is_where_modifier<modifier_type>; | |||
using is_where_type = modifier::is_where<modifier_type>; | |||
hana::eval_if( | |||
is_where_type { }, | |||
[&](auto _){ | |||
@@ -44,10 +44,11 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_dataset, typename T_modifiers> | |||
inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const | |||
{ | |||
auto& where = build_where(_schema, modifiers); | |||
auto& limit = build_limit(modifiers); | |||
std::cout << "WHERE = " << where.query(_connection) << std::endl; | |||
std::cout << "LIMIT = " << limit.query(_connection) << std::endl; | |||
read_context context(dataset, _schema, _connection, _filter); | |||
context.where = build_where(_schema, modifiers).query(_connection); | |||
context.limit = build_limit(modifiers).query(_connection); | |||
context.order_by = build_order_by(_schema, modifiers).query(_connection); | |||
read_impl_t<T_dataset>::apply(context); | |||
} | |||
}; | |||
@@ -39,7 +39,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
value_t value_field_t<T_field> | |||
::get(const data_context& context) const | |||
{ | |||
auto& dataset = context.get<dataset_type>(); | |||
auto& dataset = context.get<dataset_type>(this->table); | |||
return type_props::convert_from(this->field.getter(dataset)); | |||
} | |||
@@ -47,7 +47,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
void value_field_t<T_field> | |||
::set(const data_context& context, const value_t& value) const | |||
{ | |||
auto& dataset = context.get<dataset_type>(); | |||
auto& dataset = context.get<dataset_type>(this->table); | |||
this->field.setter(dataset, type_props::convert_to(value)); | |||
} | |||
@@ -16,12 +16,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||
using field_set_type = std::set<const field_t*>; | |||
using table_set_type = std::set<const table_t*>; | |||
size_t cache_id; | |||
size_t cache_id { 0 }; | |||
field_set_type fields; | |||
table_set_type tables; | |||
bool contains(const table_t* table, bool check_base) const; | |||
bool contains(const field_t* field) const; | |||
inline bool is_excluded(const table_t& table) const | |||
{ return tables.count(&table); } | |||
inline bool is_excluded(const field_t& field) const | |||
{ return fields.count(&field); } | |||
}; | |||
} |
@@ -76,8 +76,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
inline decltype(auto) create_update(const create_update_context& context) const | |||
{ return create_update_intern(context); } | |||
inline void read() const | |||
{ } | |||
inline void read(const read_context& context) const | |||
{ return read_exec(context); } | |||
private: | |||
template<typename T_schema, typename T_table, typename T_base_dataset> | |||
@@ -87,14 +87,18 @@ beg_namespace_cpphibernate_driver_mariadb | |||
friend struct table_polymorphic_t; | |||
using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | |||
using statement_map = std::map<size_t, ::cppmariadb::statement>; | |||
mutable statement_ptr _statement_create_table; | |||
mutable statement_ptr _statement_alter_table; | |||
mutable statement_ptr _statement_insert_into; | |||
mutable statement_map _statement_select_static; | |||
mutable statement_map _statement_select_dynamic; | |||
::cppmariadb::statement& get_statement_create_table() const; | |||
::cppmariadb::statement* get_statement_alter_table() const; | |||
::cppmariadb::statement& get_statement_insert_into() const; | |||
::cppmariadb::statement& get_statement_select(const read_context& context) const; | |||
std::string execute_create_update( | |||
const create_update_context& context, | |||
@@ -109,6 +113,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
virtual std::string create_update_intern(const create_update_context& context) const; | |||
std::string create_update_exec (const create_update_context& context) const; | |||
void read_exec (const read_context& context) const; | |||
}; | |||
/* table_simple_t */ | |||
@@ -12,10 +12,40 @@ beg_namespace_cpphibernate_misc | |||
/* container_helper */ | |||
template<typename T_container, typename = void> | |||
struct container_helper | |||
struct container_helper; | |||
template<typename T_value, typename... T_args> | |||
struct container_helper<std::vector<T_value, T_args...>, void> | |||
{ | |||
using container_type = std::vector<T_value, T_args...>; | |||
using value_type = T_value; | |||
template<typename... T_xargs> | |||
static inline value_type& emplace(container_type& container, T_xargs&&... args) | |||
{ | |||
container.emplace_back(std::forward<T_xargs>(args)...); | |||
return container.back(); | |||
} | |||
static inline void clear(container_type& container) | |||
{ container.clear(); } | |||
}; | |||
template<typename T_value, typename... T_args> | |||
struct container_helper<std::list<T_value, T_args...>, void> | |||
{ | |||
using container_type = T_container; | |||
using value_type = real_dataset_t<container_type>; | |||
using container_type = std::list<T_value, T_args...>; | |||
using value_type = T_value; | |||
template<typename... T_xargs> | |||
static inline value_type& emplace(container_type& container, T_xargs&&... args) | |||
{ | |||
container.emplace_back(std::forward<T_xargs>(args)...); | |||
return container.back(); | |||
} | |||
static inline void clear(container_type& container) | |||
{ container.clear(); } | |||
}; | |||
} |
@@ -4,4 +4,5 @@ | |||
#include <cpphibernate/modifier/modifier.h> | |||
#include <cpphibernate/modifier/modifiers.h> | |||
#include <cpphibernate/modifier/offset.h> | |||
#include <cpphibernate/modifier/order_by.h> | |||
#include <cpphibernate/modifier/where.h> |
@@ -19,32 +19,6 @@ beg_namespace_cpphibernate_modifier | |||
using value_type = T_value; | |||
value_type value; | |||
constexpr limit_t(T_value&& p_value) | |||
: value(std::forward<T_value>(p_value)) | |||
{ } | |||
}; | |||
/* limit_builder */ | |||
template<typename X, typename = void> | |||
struct limit_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::limit(...)!"); } | |||
}; | |||
template<typename T_value> | |||
struct limit_builder< | |||
mp::list<T_value>, | |||
mp::enable_if<mp::is_integral<T_value>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_value&& value) | |||
{ | |||
using value_type = mp::integral_constant<T_value, value>; | |||
return limit_t<value_type>(value_type { }); | |||
} | |||
}; | |||
} | |||
@@ -58,7 +32,8 @@ beg_namespace_cpphibernate_modifier | |||
/* make */ | |||
constexpr decltype(auto) limit = misc::make_generic_predicate<__impl::limit_builder> { }; | |||
template<size_t T_value> | |||
constexpr decltype(auto) limit = __impl::limit_t<hana::size_t<T_value>> { }; | |||
} | |||
end_namespace_cpphibernate_modifier |
@@ -19,32 +19,6 @@ beg_namespace_cpphibernate_modifier | |||
using value_type = T_value; | |||
value_type value; | |||
constexpr offset_t(T_value&& p_value) | |||
: value(std::forward<T_value>(p_value)) | |||
{ } | |||
}; | |||
/* offset_builder */ | |||
template<typename X, typename = void> | |||
struct offset_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::offset(...)!"); } | |||
}; | |||
template<typename T_value> | |||
struct offset_builder< | |||
mp::list<T_value>, | |||
mp::enable_if<mp::is_integral<T_value>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_value&& value) | |||
{ | |||
using value_type = mp::integral_constant<T_value, value>; | |||
return offset_t<value_type>(value_type { }); | |||
} | |||
}; | |||
} | |||
@@ -52,13 +26,14 @@ beg_namespace_cpphibernate_modifier | |||
/* meta */ | |||
template<typename T> | |||
struct is_offset_modifier | |||
struct is_offset | |||
: public mp::is_specialization_of<T, __impl::offset_t> | |||
{ }; | |||
/* make */ | |||
constexpr decltype(auto) offset = misc::make_generic_predicate<__impl::offset_builder> { }; | |||
template<size_t T_value> | |||
constexpr decltype(auto) offset = __impl::offset_t<hana::size_t<T_value>> { }; | |||
} | |||
end_namespace_cpphibernate_modifier |
@@ -0,0 +1,171 @@ | |||
#pragma once | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier/modifier.h> | |||
beg_namespace_cpphibernate_modifier | |||
{ | |||
namespace __impl | |||
{ | |||
/* order_direction_tag_t */ | |||
struct order_direction_tag_t | |||
{ }; | |||
/* order_direction_t */ | |||
template<typename T_field> | |||
struct order_direction_t | |||
: public order_direction_tag_t | |||
{ | |||
using field_type = T_field; | |||
using wrapped_field_type = hana::type<mp::decay_t<field_type>>; | |||
wrapped_field_type wrapped_field; | |||
}; | |||
} | |||
/* meta */ | |||
template<typename T> | |||
struct is_order_direction | |||
: public mp::is_base_of<__impl::order_direction_tag_t, T> | |||
{ }; | |||
template<typename... T> | |||
struct all_are_order_directions | |||
: public mp::all_true<is_order_direction<T>::value...> | |||
{ }; | |||
namespace __impl | |||
{ | |||
/* order_ascending_t */ | |||
template<typename T_field> | |||
struct order_ascending_t | |||
: public order_direction_t<T_field> | |||
{ }; | |||
/* order_descending_t */ | |||
template<typename T_field> | |||
struct order_descending_t | |||
: public order_direction_t<T_field> | |||
{ }; | |||
/* order_by_t */ | |||
template<typename... T_fields> | |||
struct order_by_t | |||
: public modifier_t | |||
{ | |||
using fields_type = hana::basic_tuple<T_fields...>; | |||
fields_type fields; | |||
}; | |||
} | |||
/* meta */ | |||
template<typename T> | |||
struct is_order_by | |||
: public mp::is_specialization_of<T, __impl::order_by_t> | |||
{ }; | |||
template<typename T> | |||
struct is_ascending | |||
: public mp::is_specialization_of<T, __impl::order_ascending_t> | |||
{ }; | |||
template<typename T> | |||
struct is_descending | |||
: public mp::is_specialization_of<T, __impl::order_descending_t> | |||
{ }; | |||
namespace __impl | |||
{ | |||
/* ascending_builder */ | |||
template<typename X, typename = void> | |||
struct ascending_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::ascending(...)!"); } | |||
}; | |||
template<typename T_field> | |||
struct ascending_builder< | |||
mp::list<T_field>, | |||
mp::enable_if<schema::is_field<mp::decay_t<T_field>>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_field&&) | |||
{ | |||
using field_type = mp::decay_t<T_field>; | |||
using ascending_type = order_ascending_t<field_type>; | |||
return ascending_type { }; | |||
} | |||
}; | |||
/* descending_builder */ | |||
template<typename X, typename = void> | |||
struct descending_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::descending(...)!"); } | |||
}; | |||
template<typename T_field> | |||
struct descending_builder< | |||
mp::list<T_field>, | |||
mp::enable_if<schema::is_field<mp::decay_t<T_field>>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_field&&) | |||
{ | |||
using field_type = mp::decay_t<T_field>; | |||
using descending_type = order_descending_t<field_type>; | |||
return descending_type { }; | |||
} | |||
}; | |||
/* order_by_builder */ | |||
template<typename X, typename = void> | |||
struct order_by_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::order_by(...)!"); } | |||
}; | |||
template<typename... T_order_directions> | |||
struct order_by_builder< | |||
mp::list<T_order_directions...>, | |||
mp::enable_if<all_are_order_directions<mp::decay_t<T_order_directions>...>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_order_directions&&...) | |||
{ | |||
using order_by_type = order_by_t<mp::decay_t<T_order_directions>...>; | |||
return order_by_type { }; | |||
} | |||
}; | |||
} | |||
/* make */ | |||
constexpr decltype(auto) ascending = misc::make_generic_predicate<__impl::ascending_builder> { }; | |||
constexpr decltype(auto) descending = misc::make_generic_predicate<__impl::descending_builder> { }; | |||
constexpr decltype(auto) order_by = misc::make_generic_predicate<__impl::order_by_builder> { }; | |||
} | |||
end_namespace_cpphibernate_modifier |
@@ -32,7 +32,7 @@ beg_namespace_cpphibernate_modifier | |||
struct where_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&... args) | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::where(...)!"); } | |||
}; | |||
@@ -48,7 +48,7 @@ beg_namespace_cpphibernate_modifier | |||
/* meta */ | |||
template<typename T> | |||
struct is_where_modifier | |||
struct is_where | |||
: public mp::is_specialization_of<T, __impl::where_t> | |||
{ }; | |||
@@ -1,19 +0,0 @@ | |||
#include <cpphibernate/driver/mariadb/schema/table.h> | |||
#include <cpphibernate/driver/mariadb/schema/filter.h> | |||
using namespace ::cpphibernate::driver::mariadb_impl; | |||
bool filter_t::contains(const table_t* table, bool check_base) const | |||
{ | |||
if (tables.count(table)) | |||
return true; | |||
else if (check_base && table->base_table) | |||
return contains(table->base_table, true); | |||
else | |||
return false; | |||
} | |||
bool filter_t::contains(const field_t* field) const | |||
{ | |||
return (fields.count(field) > 0); | |||
} |
@@ -12,6 +12,173 @@ | |||
using namespace ::utl; | |||
using namespace ::cpphibernate::driver::mariadb_impl; | |||
/* data_extractor_t */ | |||
struct data_extractor_t | |||
{ | |||
const table_t& _table; | |||
const read_context& _context; | |||
const ::cppmariadb::row& _row; | |||
mutable size_t _index; | |||
data_extractor_t( | |||
const table_t& p_table, | |||
const read_context& p_context, | |||
const ::cppmariadb::row& p_row) | |||
: _table (p_table) | |||
, _context (p_context) | |||
, _row (p_row) | |||
{ } | |||
inline value_t get_value() const | |||
{ | |||
value_t ret; | |||
auto f = _row.at(_index); | |||
if (!f.is_null()) | |||
ret = f.get<std::string>(); | |||
return ret; | |||
} | |||
inline void read_field(const field_t& field) const | |||
{ | |||
field.set(_context, get_value()); | |||
++_index; | |||
} | |||
inline void read_table(const table_t& table) const | |||
{ | |||
if (table.base_table) | |||
read_table(*table.base_table); | |||
if (_context.filter.is_excluded(table)) | |||
return; | |||
/* primary key */ | |||
assert(table.primary_key_field); | |||
read_field(*table.primary_key_field); | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (!_context.filter.is_excluded(field)) | |||
read_field(field); | |||
} | |||
} | |||
inline void operator()() const | |||
{ | |||
_index = 0; | |||
_context.emplace(); | |||
read_table(_table); | |||
} | |||
}; | |||
/* select_query_builder_t */ | |||
struct select_query_builder_t | |||
{ | |||
const table_t& _table; | |||
const filter_t& _filter; | |||
bool _is_dynamic; | |||
size_t index { 0 }; | |||
std::ostringstream os; | |||
std::ostringstream join; | |||
select_query_builder_t( | |||
const table_t& p_table, | |||
const filter_t& p_filter, | |||
bool p_is_dynamic) | |||
: _table (p_table) | |||
, _filter (p_filter) | |||
, _is_dynamic(p_is_dynamic) | |||
{ } | |||
inline void add_field(const field_t& field) | |||
{ | |||
if (index++) os << ", "; | |||
os << "`" | |||
<< field.table_name | |||
<< "`.`" | |||
<< field.field_name | |||
<< "`"; | |||
} | |||
inline bool add_table(const table_t& table, const std::string& prefix) | |||
{ | |||
bool ret = false; | |||
if (table.base_table) | |||
{ | |||
auto tmp = add_table(*table.base_table, ""); | |||
if (tmp) | |||
{ | |||
assert(table.base_table->primary_key_field); | |||
auto& base_key = *table.base_table->primary_key_field; | |||
ret = true; | |||
join << " LEFT JOIN `" | |||
<< table.table_name | |||
<< "` ON `" | |||
<< table.table_name | |||
<< "`.`" | |||
<< base_key.field_name | |||
<< "`=`" | |||
<< base_key.table_name | |||
<< "`.`" | |||
<< base_key.field_name | |||
<< "`"; | |||
} | |||
} | |||
/* __type */ | |||
if ( _is_dynamic | |||
&& !table.base_table | |||
&& !table.derived_tables.empty()) | |||
{ | |||
if (index++) os << ", "; | |||
os << "`" | |||
<< table.table_name | |||
<< "`.`__type` AS `__type`"; | |||
ret = true; | |||
} | |||
if (_filter.is_excluded(table)) | |||
return ret; | |||
ret = true; | |||
/* primary key */ | |||
assert(table.primary_key_field); | |||
add_field(*table.primary_key_field); | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (!_filter.is_excluded(field)) | |||
add_field(field); | |||
} | |||
return ret; | |||
} | |||
inline std::string operator()() | |||
{ | |||
os << "SELECT "; | |||
add_table(_table, ""); | |||
os << " FROM `" | |||
<< _table.table_name | |||
<< "`" | |||
<< join.str() | |||
<< " ?where! ?order! ?limit!"; | |||
return os.str(); | |||
} | |||
}; | |||
/* build queries */ | |||
std::string build_init_stage1_query(const table_t& table) | |||
@@ -380,7 +547,7 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
/* base table key fields */ | |||
if ( static_cast<bool>(table.base_table) | |||
&& ( !is_update | |||
|| filter->contains(table.base_table, true))) | |||
|| !filter->is_excluded(*table.base_table))) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
@@ -401,11 +568,11 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
if (is_update && !filter->contains(ptr)) | |||
auto& field_info = *ptr; | |||
if (is_update && filter->is_excluded(field_info)) | |||
continue; | |||
if (index++) | |||
os << ", "; | |||
auto& field_info = *ptr; | |||
assert(field_info.referenced_table); | |||
assert(field_info.referenced_table->primary_key_field); | |||
auto& key_info = *field_info.referenced_table->primary_key_field; | |||
@@ -462,12 +629,12 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
if (is_update && !filter->contains(ptr)) | |||
assert(ptr); | |||
auto& field_info = *ptr; | |||
if (is_update && filter->is_excluded(field_info)) | |||
continue; | |||
if (index++) | |||
os << ", "; | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
os << "`" | |||
<< field_info.field_name | |||
<< "`=" | |||
@@ -505,6 +672,14 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
return os.str(); | |||
} | |||
std::string build_select_query( | |||
const table_t& table, | |||
const filter_t& filter, | |||
bool is_dynamic) | |||
{ | |||
return select_query_builder_t(table, filter, is_dynamic)(); | |||
} | |||
/* execute_create_update */ | |||
std::string table_t::execute_create_update( | |||
@@ -537,7 +712,7 @@ std::string table_t::execute_create_update( | |||
/* base_key */ | |||
if ( base_table | |||
&& ( !is_update | |||
|| filter->contains(base_table, true))) | |||
|| !filter->is_excluded(*base_table))) | |||
{ | |||
auto new_context = context; | |||
if (!new_context.derived_table) | |||
@@ -547,14 +722,14 @@ std::string table_t::execute_create_update( | |||
++index; | |||
} | |||
if (is_update && !filter->contains(this, false)) | |||
if (is_update && filter->is_excluded(*this)) | |||
return primary_key; | |||
/* foreign table one fields */ | |||
for (auto& ptr : foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
if (is_update && !filter->contains(ptr)) | |||
if (is_update && filter->is_excluded(*ptr)) | |||
continue; | |||
value_t key = ptr->foreign_create_update(context); | |||
if (key.has_value()) statement.set(index, std::move(key)); | |||
@@ -596,9 +771,9 @@ std::string table_t::execute_create_update( | |||
/* data fields */ | |||
for (auto& ptr : data_fields) | |||
{ | |||
if (is_update && !filter->contains(ptr)) | |||
continue; | |||
assert(ptr); | |||
if (is_update && filter->is_excluded(*ptr)) | |||
continue; | |||
auto& field_info = *ptr; | |||
auto value = field_info.get(context); | |||
@@ -654,8 +829,8 @@ std::string table_t::execute_create_update( | |||
{ | |||
assert(ptr); | |||
if ( is_update | |||
&& ( !filter->contains(ptr) | |||
|| !filter->contains(ptr->referenced_table, true))) | |||
&& ( filter->is_excluded(*ptr) | |||
|| filter->is_excluded(*ptr->referenced_table))) | |||
continue; | |||
auto next_context = context; | |||
@@ -750,6 +925,20 @@ const table_t* table_t::get_derived(size_t id) const | |||
return *_statement_create_table; | |||
} | |||
::cppmariadb::statement& table_t::get_statement_select(const read_context& context) const | |||
{ | |||
auto& map = context.is_dynamic | |||
? _statement_select_dynamic | |||
: _statement_select_static; | |||
auto it = map.find(context.filter.cache_id); | |||
if (it == map.end()) | |||
{ | |||
auto query = build_select_query(*this, context.filter, context.is_dynamic); | |||
it = map.emplace(context.filter.cache_id, ::cppmariadb::statement(query)).first; | |||
} | |||
return it->second; | |||
} | |||
std::string table_t::create_update_base(const create_update_context& context) const | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
@@ -780,4 +969,19 @@ std::string table_t::create_update_exec(const create_update_context& context) co | |||
} | |||
std::string table_t::create_update_intern(const create_update_context& context) const | |||
{ return create_update_exec(context); } | |||
{ return create_update_exec(context); } | |||
void table_t::read_exec(const read_context& context) const | |||
{ | |||
auto& statement = get_statement_select(context); | |||
auto& connection = context.connection; | |||
cpphibernate_debug_log("execute SELECT query: " << statement.query(connection)); | |||
auto result = connection.execute_used(statement); | |||
if (!result) | |||
throw misc::hibernate_exception("Unable to fetching data from database!"); | |||
::cppmariadb::row* row; | |||
while ((row = result->next())) | |||
data_extractor_t(*this, context, *row)(); | |||
context.finish(); | |||
} |
@@ -6,13 +6,27 @@ | |||
using namespace ::testing; | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::modifier; | |||
using namespace ::boost::hana::literals; | |||
TEST(CppHibernateTests, read_test1) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
// expect_query(mock, "START TRANSACTION"); | |||
// expect_query(mock, "COMMIT"); | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "SELECT " | |||
"`tbl_test1`.`tbl_test1_id`, " | |||
"`tbl_test1`.`str_data`, " | |||
"`tbl_test1`.`str64_data`, " | |||
"`tbl_test1`.`u32_nullable`, " | |||
"`tbl_test1`.`u32_ptr_u`, " | |||
"`tbl_test1`.`u32_ptr_s` " | |||
"FROM " | |||
"`tbl_test1` ", | |||
result_used({ | |||
{ "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" } | |||
})); | |||
expect_query(mock, "COMMIT"); | |||
EXPECT_CALL( | |||
mock, | |||
@@ -26,9 +40,105 @@ TEST(CppHibernateTests, read_test1) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
test1 t1; | |||
t1.id = uuid("1e133ad8-ad2e-11e8-98d0-529269fb1459"); | |||
t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
using namespace modifier; | |||
context.read(t1); | |||
} | |||
EXPECT_EQ (t1.str_data, "str_data of class `test1` object `t1`"); | |||
EXPECT_EQ (t1.str64_data, "str64_data of class `test1` object `t1`"); | |||
EXPECT_FALSE(static_cast<bool>(t1.u32_nullable)); | |||
ASSERT_TRUE (static_cast<bool>(t1.u32_ptr_u)); | |||
EXPECT_EQ (*t1.u32_ptr_u, 123); | |||
ASSERT_TRUE (static_cast<bool>(t1.u32_ptr_s)); | |||
EXPECT_EQ (*t1.u32_ptr_s, 456); | |||
} | |||
TEST(CppHibernateTests, read_test2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "SELECT " | |||
"`tbl_test2`.`tbl_test2_id`, " | |||
"`tbl_test2`.`u8_data`, " | |||
"`tbl_test2`.`i8_data`, " | |||
"`tbl_test2`.`u16_data`, " | |||
"`tbl_test2`.`i16_data` " | |||
"FROM " | |||
"`tbl_test2` ", | |||
result_used({ | |||
{ "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" } | |||
})); | |||
expect_query(mock, "COMMIT"); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
.Times(AnyNumber()) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
constexpr decltype(auto) test2_key_field = test_schema.tables[1_c].fields[0_c]; | |||
test2 t2; | |||
context.read(t2, where(equal(test2_key_field, "3d1270dc-abb9-11e8-98d0-529269fb1459"))); | |||
EXPECT_EQ(1, t2.u8_data); | |||
EXPECT_EQ(2, t2.i8_data); | |||
EXPECT_EQ(3, t2.u16_data); | |||
EXPECT_EQ(4, t2.i16_data); | |||
} | |||
TEST(CppHibernateTests, read_test3) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "SELECT " | |||
"`tbl_test3`.`tbl_test3_id`, " | |||
"`tbl_test3`.`u32_data`, " | |||
"`tbl_test3`.`i32_data`, " | |||
"`tbl_test3`.`u64_data`, " | |||
"`tbl_test3`.`i64_data` " | |||
"FROM " | |||
"`tbl_test3` ", | |||
result_used({ | |||
{ "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" } | |||
})); | |||
expect_query(mock, "COMMIT"); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
.Times(AnyNumber()) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
test3 t3; | |||
t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||
context.read(t3); | |||
EXPECT_EQ(5, t3.u32_data); | |||
EXPECT_EQ(6, t3.i32_data); | |||
EXPECT_EQ(7, t3.u64_data); | |||
EXPECT_EQ(8, t3.i64_data); | |||
} |
@@ -24,7 +24,7 @@ ACTION(EscapeString) | |||
struct result_data | |||
: public mariadb_mock_item | |||
{ | |||
using row_type = std::vector<std::string>; | |||
using row_type = std::vector<const char *>; | |||
using data_type = std::vector<row_type>; | |||
struct internal_data_t | |||
@@ -55,8 +55,8 @@ struct result_data | |||
for (size_t j = 0; j < d.size(); ++j) | |||
{ | |||
auto& str = d.at(j); | |||
intern.data[j] = const_cast<char*>(str.c_str()); | |||
intern.length[j] = static_cast<unsigned long>(str.size()); | |||
intern.data[j] = const_cast<char*>(str); | |||
intern.length[j] = static_cast<unsigned long>(str ? strlen(str) : 0); | |||
} | |||
} | |||
} | |||