@@ -60,7 +60,7 @@ int main(int argc, char** argv) | |||
connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty()); | |||
/* create a hibernation context */ | |||
auto context = make_context_ptr<driver::mariadb>(test_schema, c); | |||
auto context = make_context_ptr<mariadb_driver>(test_schema, c); | |||
/* initialize the database schema */ | |||
context.init(); /* CREATE SCHEMA IF NOT EXISTS `test_schema` DEFAULT CHARACTER SET utf8; | |||
@@ -327,7 +327,7 @@ int main(int argc, char** argv) | |||
connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty()); | |||
/* create a hibernation context */ | |||
auto context = make_context_ptr<driver::mariadb>(test_schema, c); | |||
auto context = make_context_ptr<mariadb_driver>(test_schema, c); | |||
/* initialize the database schema */ | |||
context.init(); | |||
@@ -405,4 +405,4 @@ int main(int argc, char** argv) | |||
## License | |||
This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details | |||
This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details |
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include <cpphibernate/modifier.h> | |||
#include "context.h" | |||
namespace cpphibernate | |||
@@ -18,19 +20,20 @@ namespace cpphibernate | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::init(...)!"); } | |||
}; | |||
constexpr decltype(auto) init = mp::generic_predicate<init_builder> { }; | |||
template<typename T_impl, typename T_bool> | |||
struct init_builder< | |||
mp::list<T_impl, T_bool>, | |||
mp::enable_if_t< | |||
mp::is_valid_v<decltype(std::declval<T_impl>().init(std::declval<bool>()))> | |||
&& mp::is_same_v<bool, mp::decay_t<T_bool>>>> | |||
&& mp::is_same_v<bool, mp::decay_t<T_bool>> | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, bool recreate) | |||
{ return impl.init(recreate); } | |||
}; | |||
constexpr decltype(auto) init = mp::generic_predicate<init_builder> { }; | |||
/* create_builder */ | |||
template<typename X, typename = void> | |||
@@ -41,18 +44,19 @@ namespace cpphibernate | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::create(...)!"); } | |||
}; | |||
constexpr decltype(auto) create = mp::generic_predicate<create_builder> { }; | |||
template<typename T_impl, typename T_dataset> | |||
struct create_builder< | |||
mp::list<T_impl, T_dataset>, | |||
mp::enable_if_t< | |||
mp::is_valid_v<decltype(std::declval<T_impl>().create(std::declval<T_dataset&>()))>>> | |||
mp::is_valid_v<decltype(std::declval<T_impl>().create(std::declval<T_dataset&>()))> | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) | |||
{ return impl.create(dataset); } | |||
}; | |||
constexpr decltype(auto) create = mp::generic_predicate<create_builder> { }; | |||
/* read_builder */ | |||
template<typename X, typename = void> | |||
@@ -64,16 +68,71 @@ namespace cpphibernate | |||
}; | |||
constexpr decltype(auto) read = mp::generic_predicate<read_builder> { }; | |||
#if 0 | |||
template<typename T_impl, typename T_dataset, typename... T_modifiers> | |||
struct create_impl< | |||
mp::list<T_impl, T_dataset, T_modifiers...>, | |||
mp::enable_if_t<mp::is_true_v<is_modifier_v<mp::decay_t<T_modifiers>>...>>> | |||
template<typename T_schema, typename T_impl, typename T_dataset, typename T_modifiers> | |||
struct read_builder< | |||
mp::list<T_schema, T_impl, T_dataset, T_modifiers>, | |||
mp::enable_if_t< | |||
schema::is_schema_v<mp::decay_t<T_schema>> | |||
&& is_modifiers_v<mp::decay_t<T_modifiers>> | |||
&& mp::is_valid_v<decltype(std::declval<T_impl>().read( | |||
std::declval<T_dataset&>(), | |||
std::declval<T_modifiers>()))> | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifiers&& modifiers) | |||
{ return impl.read(dataset, std::forward<T_modifiers>(modifiers)); } | |||
}; | |||
template<typename T_schema, typename T_impl, typename T_dataset> | |||
struct read_builder< | |||
mp::list<T_schema, T_impl, T_dataset>, | |||
mp::enable_if_t< | |||
schema::is_schema_v<mp::decay_t<T_schema>> | |||
&& !is_container_v<mp::decay_t<T_dataset>> | |||
&& !is_nullable_v<mp::decay_t<T_dataset>> | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset, T_modifiers&&... modifiers) | |||
{ return impl.read(dataset, make_modifiers(std::forward<T_modifiers>(modifiers)...)); } | |||
static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset) | |||
{ | |||
using real_dataset_type = real_dataset_t<mp::decay_t<T_dataset>>; | |||
auto& table = schema::find_table(schema.tables, hana::type_c<real_dataset_type>); | |||
auto& primary_key = schema::get_primary_key_field(table); | |||
return impl.read(dataset, make_modifiers(where(equal(primary_key, primary_key.getter(dataset))))); | |||
} | |||
}; | |||
#endif | |||
template<typename T_schema, typename T_impl, typename T_dataset, typename... T_modifier> | |||
struct read_builder< | |||
mp::list<T_schema, T_impl, T_dataset, T_modifier...>, | |||
mp::enable_if_t< | |||
schema::is_schema_v<mp::decay_t<T_schema>> | |||
&& mp::is_true_v<is_modifier_v<T_modifier>...> | |||
&& ( is_container_v<mp::decay_t<T_dataset>> | |||
|| is_nullable_v<mp::decay_t<T_dataset>>) | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifier&&... modifier) | |||
{ return impl.read(dataset, make_modifiers(std::forward<T_modifier>(modifier)...)); } | |||
}; | |||
template<typename T_schema, typename T_impl, typename T_dataset, typename... T_modifier> | |||
struct read_builder< | |||
mp::list<T_schema, T_impl, T_dataset, T_modifier...>, | |||
mp::enable_if_t< | |||
schema::is_schema_v<mp::decay_t<T_schema>> | |||
&& mp::is_true_v<is_modifier_v<T_modifier>...> | |||
&& !is_container_v<mp::decay_t<T_dataset>> | |||
&& !is_nullable_v<mp::decay_t<T_dataset>> | |||
&& sizeof...(T_modifier) | |||
>> | |||
{ | |||
static constexpr decltype(auto) apply(const T_schema& schema, T_impl& impl, T_dataset& dataset, T_modifier&&... modifier) | |||
{ return impl.read(dataset, make_modifiers(std::forward<T_modifier>(modifier)...)); } | |||
}; | |||
/* update_builder */ | |||
template<typename X, typename = void> | |||
@@ -85,14 +144,17 @@ namespace cpphibernate | |||
}; | |||
constexpr decltype(auto) update = mp::generic_predicate<update_builder> { }; | |||
#if 0 | |||
template<typename T_impl, typename T_dataset> | |||
struct update_impl<mp::list<T_impl, T_dataset>, void> | |||
struct update_builder< | |||
mp::list<T_impl, T_dataset>, | |||
mp::enable_if_t< | |||
mp::is_valid_v<decltype(std::declval<T_impl>().update(std::declval<T_dataset&>()))>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) | |||
{ return impl.update(dataset); } | |||
}; | |||
#endif | |||
/* destroy_builder */ | |||
template<typename X, typename = void> | |||
@@ -104,14 +166,16 @@ namespace cpphibernate | |||
}; | |||
constexpr decltype(auto) destroy = mp::generic_predicate<destroy_builder> { }; | |||
#if 0 | |||
template<typename T_impl, typename T_dataset> | |||
struct destroy_impl<mp::list<T_impl, T_dataset>, void> | |||
struct destroy_builder< | |||
mp::list<T_impl, T_dataset>, | |||
mp::enable_if_t< | |||
mp::is_valid_v<decltype(std::declval<T_impl>().destroy(std::declval<T_dataset&>()))>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) | |||
{ return impl.create(dataset); } | |||
{ return impl.destroy(dataset); } | |||
}; | |||
#endif | |||
} | |||
@@ -137,7 +201,7 @@ namespace cpphibernate | |||
template<typename T_driver, typename T_schema> | |||
template<typename... T_args> | |||
constexpr decltype(auto) context<T_driver, T_schema>::read(T_args&&... args) | |||
{ return __impl::read(this->impl(), std::forward<T_args>(args)...); } | |||
{ return __impl::read(_schema, this->impl(), std::forward<T_args>(args)...); } | |||
template<typename T_driver, typename T_schema> | |||
template<typename... T_args> | |||
@@ -149,45 +213,6 @@ namespace cpphibernate | |||
constexpr decltype(auto) context<T_driver, T_schema>::destroy(T_args&&... args) | |||
{ return __impl::destroy(this->impl(), std::forward<T_args>(args)...); } | |||
#if 0 | |||
template<typename T_dataset, typename... T_modifiers> | |||
constexpr auto read(T_dataset& dataset, T_modifiers&&... modifiers) | |||
-> mp::enable_if< | |||
modifier::all_are_modifiers<mp::decay_t<T_modifiers>...>> | |||
{ | |||
using namespace modifier; | |||
using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
this->read_impl(dataset, modifier::make_list(std::forward<T_modifiers>(modifiers)...)); | |||
} | |||
template<typename T_dataset> | |||
constexpr auto read(T_dataset& dataset) | |||
-> mp::enable_if_c< | |||
!misc::is_container<mp::decay_t<T_dataset>>::value | |||
&& !misc::is_nullable<mp::decay_t<T_dataset>>::value> | |||
{ | |||
using namespace modifier; | |||
using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
auto& table = schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
auto& primary_key = schema::table::get_primary_key_field(table); | |||
this->read_impl(dataset, modifier::make_list(where(equal(primary_key, primary_key.getter(dataset))))); | |||
} | |||
template<typename T_dataset> | |||
constexpr auto read(T_dataset& dataset) | |||
-> mp::enable_if_c< | |||
misc::is_container<mp::decay_t<T_dataset>>::value | |||
|| misc::is_nullable<mp::decay_t<T_dataset>>::value> | |||
{ | |||
using namespace modifier; | |||
using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
this->read_impl(dataset, modifier::make_list()); | |||
} | |||
#endif | |||
/* make_context */ | |||
template<typename T_driver, typename T_schema, typename... T_args> | |||
@@ -11,6 +11,9 @@ namespace mariadb { | |||
struct table_t; | |||
struct data_context; | |||
struct create_update_context; | |||
struct read_context; | |||
using read_context_ptr_u = std::unique_ptr<read_context>; | |||
/** | |||
* @brief Abstract field class. | |||
@@ -128,11 +131,66 @@ namespace mariadb { | |||
*/ | |||
virtual value_t foreign_create_update(const create_update_context& context) const; | |||
/** | |||
* @brief Create a read context for the foreign key field. | |||
* | |||
* @param[in] context Read context to inherit new context from. | |||
* @param[in] create_fake Create a fake context (not data will be written). | |||
* | |||
* @return The created read context. | |||
*/ | |||
virtual read_context_ptr_u foreign_read(const read_context& context, bool create_fake) const; | |||
/** | |||
* @brief Delete all old datasets from the foreign table. | |||
* | |||
* If we update an exsisting foreign field with a new foreign dataset, the key of this dataset | |||
* changes. So we need to delete the old/exsisting foreign dataset from the database. | |||
* | |||
* @param[in] context Create/Update context with the needed data for the operation. | |||
* @param[in] primary_key Primary key of the current database. | |||
* @param[in] foreign_key Primary kes of the new foreign dataset. | |||
*/ | |||
void foreign_one_delete( | |||
const create_update_context& context, | |||
const std::string& primary_key, | |||
const value_t& foreign_key) const; | |||
/** | |||
* @brief Update the foreign dataset. Set the foreign key field to NULL if it matches the passed | |||
* primary key of the owner dataset. | |||
*/ | |||
void foreign_many_update( | |||
const create_update_context& context, | |||
const std::string& primary_key) const; | |||
private: | |||
/** | |||
* @brief Initialize the field. | |||
*/ | |||
void init(); | |||
private: | |||
using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; | |||
mutable statement_ptr_u _statement_foreign_one_delete_key_known; //!< Statement to delete foreign datasets within an update operation (for known foreign keys) | |||
mutable statement_ptr_u _statement_foreign_one_delete_key_unknown; //!< Statement to delete foreign datasets within an update operation (for unknown foreign keys) | |||
mutable statement_ptr_u _statement_foreign_many_update; //!< Statement to update foreign many dataset (set foreign key to NULL if primary key of the owner matches) | |||
/** | |||
* @brief Get the statement to delete foreign datasets within an update operation (for known foreign keys). | |||
*/ | |||
::cppmariadb::statement& get_statement_foreign_one_delete_key_known() const; | |||
/** | |||
* @brief Get the statement to delete foreign datasets within an update operation (for unknown foreign keys). | |||
*/ | |||
::cppmariadb::statement& get_statement_foreign_one_delete_key_unknown() const; | |||
/** | |||
* @brief Get the statement to update foreign many dataset (set foreign key to NULL if primary key of the owner matches). | |||
*/ | |||
::cppmariadb::statement& get_statement_foreign_many_update() const; | |||
}; | |||
using field_ptr_u = std::unique_ptr<const field_t>; | |||
@@ -32,6 +32,26 @@ namespace mariadb { | |||
const T_schema& p_schema, | |||
const T_table& p_table, | |||
const T_field& p_field); | |||
public: | |||
/** | |||
* @brief Execute a create/update operation on the foreign table this field represents (if it is a foreign field) | |||
* | |||
* @param[in] context Create/Update context with the needed data for the operation. | |||
* | |||
* @return Key of the created/updated foreign dataset. | |||
*/ | |||
inline value_t foreign_create_update(const create_update_context& context) const override; | |||
/** | |||
* @brief Create a read context for the foreign key field. | |||
* | |||
* @param[in] context Read context to inherit new context from. | |||
* @param[in] create_fake Create a fake context (not data will be written). | |||
* | |||
* @return The created read context. | |||
*/ | |||
inline read_context_ptr_u foreign_read(const read_context& context, bool create_fake) const override; | |||
}; | |||
} } |
@@ -2,6 +2,10 @@ | |||
#include "field_foreign_table.h" | |||
#include "../../impl/create_update.inl" | |||
#include "../../context/read_context.inl" | |||
#include "../../context/create_update_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -24,4 +28,46 @@ namespace mariadb { | |||
p_field) | |||
{ } | |||
template< | |||
typename T_field> | |||
value_t field_foreign_table_t<T_field> | |||
::foreign_create_update(const create_update_context& context) const | |||
{ | |||
using dataset_type = typename decltype(+this->_field.wrapped_dataset_type)::type; | |||
auto& dataset = context.get<dataset_type>(); | |||
auto& foreign = this->_field.getter(dataset); | |||
auto next_context = change_context(context, foreign); | |||
using foreign_dataset_type = mp::decay_t<decltype(foreign)>; | |||
return create_update_impl_t<foreign_dataset_type>::apply( | |||
next_context, | |||
false); | |||
} | |||
template< | |||
typename T_field> | |||
read_context_ptr_u field_foreign_table_t<T_field> | |||
::foreign_read(const read_context& context, bool create_fake) const | |||
{ | |||
using dataset_type = typename decltype(+this->_field.wrapped_dataset_type)::type; | |||
using value_type = typename decltype(+this->_field.wrapped_value_type)::type; | |||
if (create_fake) | |||
{ | |||
auto new_context = make_read_context(context.schema, context.connection, hana::type_c<value_type>, context.filter); | |||
using context_type = mp::decay_t<decltype(new_context)>; | |||
return std::make_unique<context_type>(std::move(new_context)); | |||
} | |||
else | |||
{ | |||
auto& dataset = context.get<dataset_type>(); | |||
auto& member = this->_field.getter(dataset); | |||
auto new_context = make_read_context(context.schema, context.connection, member, context.filter); | |||
using context_type = mp::decay_t<decltype(new_context)>; | |||
return std::make_unique<context_type>(std::move(new_context)); | |||
} | |||
} | |||
} } |
@@ -1,5 +1,6 @@ | |||
#pragma once | |||
#include <set> | |||
#include <string> | |||
#include <vector> | |||
#include <cppmariadb.h> | |||
@@ -12,8 +13,11 @@ namespace mariadb { | |||
struct filter_t; | |||
struct schema_t; | |||
struct base_context; | |||
struct init_context; | |||
struct read_context; | |||
struct create_update_context; | |||
struct destroy_context; | |||
enum init_stage | |||
{ | |||
@@ -31,6 +35,7 @@ namespace mariadb { | |||
using size_vector = std::vector<size_t>; //!< vector of size_t | |||
using table_vector = std::vector<const table_t *>; //!< vector of constant field pointers | |||
using field_vector = std::vector<const field_t *>; //!< vector of constant field pointers | |||
using table_set = std::set<const table_t *>; //!< set of tables | |||
public: | |||
size_t id { 0 }; //!< unique id of the table assigned by the user | |||
@@ -109,10 +114,17 @@ namespace mariadb { | |||
*/ | |||
void init(const init_context& context, init_stage stage) const; | |||
/** | |||
* @brief Execute a read operation on the current table. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
*/ | |||
void read(const read_context& context) const; | |||
/** | |||
* @brief Execute a create/update operation on the current table. | |||
* | |||
* For a polymorphic type this will check the derived tables before executing the actual opertion. | |||
* For a polymorphic type this will check the derived tables before executing the actual operation. | |||
* If the dataset matches one of the dericed tables, the operation is executed on this table. | |||
* This operation also updates the base table if the dataset has one. | |||
* | |||
@@ -122,6 +134,30 @@ namespace mariadb { | |||
*/ | |||
virtual std::string create_update(const create_update_context& context) const; | |||
/** | |||
* @brief Execute a destroy operation on the current table. | |||
* | |||
* For a polymorphic type this will check the derived tables before executing the actual operation. | |||
* If the dataset matches one of the dericed tables, the operation is executed on this table. | |||
* This operation also updates the base table if the dataset has one. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
*/ | |||
virtual void destroy(const destroy_context& context) const; | |||
/** | |||
* @brief Cleanup orphaned datasets beginning from this table. | |||
* | |||
* This operation will iterate through the different base, derived and foreign tables of this table | |||
* and delete all datasets that are not referenced at least by one other dataset. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
* @param[in] processed Contains all tables that are already cleared (to handle ring dependencies). | |||
* @param[in] check_derived Check the derived tables. | |||
* @param[in] check_base Check the base table. | |||
*/ | |||
void cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const; | |||
public: | |||
/** | |||
* @brief Get the value of the primary key of this table. | |||
@@ -145,21 +181,75 @@ namespace mariadb { | |||
/** | |||
* @brief Execute the actual create/update operation. | |||
* | |||
* Other than create_update this will not check the derived tables. It will execute the query | |||
* and forward the operation to the base table if the dataset has one. | |||
* Other than the normal create_update method this will not check the derived tables. | |||
* It will execute the query and forward the operation to the base table if the dataset has one. | |||
*/ | |||
std::string create_update_exec(const create_update_context& context) const; | |||
/** | |||
* @brief Execute the actual destroy operation. | |||
* | |||
* Other than the normal destroy method this will not check the derived tables. | |||
* It will execute the query and forward the operation to the base table if the dataset has one. | |||
*/ | |||
void destroy_exec(const destroy_context& context) const; | |||
/** | |||
* @brief Delete all datasets from the table that foreign keys are all set to NULL. | |||
* | |||
* @param[in] context Context that stores the needed data for this operation. | |||
*/ | |||
void foreign_many_delete_exec(const base_context& context) const; | |||
/** | |||
* @brief Build the delete query for this table. | |||
* | |||
* @param[in] where Where expression to add to the delete query. | |||
* | |||
* @return The requested delete query. | |||
*/ | |||
std::string build_delete_query(const std::string* where) const; | |||
public: | |||
/** | |||
* @brief Emplace new dataset of the type the table represents. | |||
* | |||
* @param[in] context Context to emplace new dataset in. | |||
*/ | |||
virtual void emplace(const read_context& context) const; | |||
/** | |||
* @brief Get the derived table by it's dataset id | |||
* | |||
* @param[in] p_id Dataset id to get derived table for. | |||
* | |||
* @return Derived table or nullptr if table was not found. | |||
*/ | |||
inline const table_t* get_derived_by_dataset_id(size_t p_id) const; | |||
/** | |||
* @brief Get the derived table by it's table id | |||
* | |||
* @param[in] p_id Table id to get derived table for. | |||
* | |||
* @return Derived table or nullptr if table was not found. | |||
*/ | |||
inline const table_t* get_derived_by_table_id(size_t p_id) const; | |||
private: | |||
using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; | |||
using statement_key = std::tuple<size_t, const field_t*>; | |||
using statement_map = std::map<statement_key, ::cppmariadb::statement>; | |||
mutable statement_ptr_u _statement_key_from_base; //!< Statement to fetch the key of this table from the base table. | |||
mutable statement_ptr_u _statement_init_stage1; //!< Statement for init stage 1 (create table). | |||
mutable statement_ptr_u _statement_init_stage2; //!< Statement for init stage 2 (alter table). | |||
mutable statement_ptr_u _statement_insert_into; //!< Statement for create operation (inser into) | |||
mutable statement_map _statement_update; //!< Map of all update statements | |||
mutable statement_ptr_u _statement_key_from_base; //!< Statement to fetch the key of this table from the base table. | |||
mutable statement_ptr_u _statement_init_stage1; //!< Statement for init stage 1 (create table). | |||
mutable statement_ptr_u _statement_init_stage2; //!< Statement for init stage 2 (alter table). | |||
mutable statement_ptr_u _statement_insert_into; //!< Statement for create operation (inser into) | |||
mutable statement_map _statement_select_static; //!< Statement to select simple datasets from the database | |||
mutable statement_map _statement_select_dynamic; //!< Statement to select dynamic/polymorphic datasets from the database | |||
mutable statement_map _statement_update; //!< Map of all update statements | |||
mutable statement_ptr_u _statement_foreign_many_delete; //!< Statement to delete all datasets from the table that foreign keys are all set to NULL. | |||
mutable statement_ptr_u _statement_delete; //!< Statement to delete datasets from the database | |||
/** | |||
* @brief Get the statement to fetch the key of this table from the base table. | |||
@@ -177,10 +267,18 @@ namespace mariadb { | |||
::cppmariadb::statement* get_statement_init_stage2() const; | |||
/** | |||
* Get the statement for the create operation. If the statement is empty nullptr is returned; | |||
* Get the statement for the create operation. If the statement is empty nullptr is returned. | |||
*/ | |||
::cppmariadb::statement* get_statement_insert_into() const; | |||
/** | |||
* Get the statement for select operations. | |||
* | |||
* @param[in] filter Filter to apply to the statement. | |||
* @param[in] dynamic Get select statement for dynamic/polymorphic datasets, for simple datasets otherwise. | |||
*/ | |||
::cppmariadb::statement& get_statement_select(const filter_t& filter, bool dynamic) const; | |||
/** | |||
* Get the statement for the update operation. If the statement is empty nullptr is returned; | |||
* | |||
@@ -188,6 +286,16 @@ namespace mariadb { | |||
* @param[in] owner Field the current dataset is owned by (if this table is used in a foreign field). | |||
*/ | |||
::cppmariadb::statement* get_statement_update(const filter_t& filter, const field_t * owner) const; | |||
/** | |||
* @brief Get the statement to delete all datasets from the table that foreign keys are all set to NULL. | |||
*/ | |||
::cppmariadb::statement& get_statement_foreign_many_delete() const; | |||
/** | |||
* @brief Get statement to delete datasets from the database. | |||
*/ | |||
::cppmariadb::statement& get_statement_delete() const; | |||
}; | |||
using table_ptr_u = std::unique_ptr<const table_t>; | |||
@@ -26,6 +26,36 @@ namespace mariadb { | |||
, fields (make_fields(*this, p_schema, p_table)) | |||
{ } | |||
const table_t* table_t | |||
::get_derived_by_dataset_id(size_t p_id) const | |||
{ | |||
if (dataset_id == p_id) | |||
return this; | |||
for (auto& ptr : derived_tables) | |||
{ | |||
assert(ptr); | |||
auto ret = ptr->get_derived_by_dataset_id(p_id); | |||
if (ret) | |||
return ret; | |||
} | |||
return nullptr; | |||
} | |||
const table_t* table_t | |||
::get_derived_by_table_id(size_t p_id) const | |||
{ | |||
if (id == p_id) | |||
return this; | |||
for (auto& ptr : derived_tables) | |||
{ | |||
assert(ptr); | |||
auto ret = ptr->get_derived_by_table_id(p_id); | |||
if (ret) | |||
return ret; | |||
} | |||
return nullptr; | |||
} | |||
namespace __impl | |||
{ | |||
@@ -69,7 +99,7 @@ namespace mariadb { | |||
mp::enable_if_t< | |||
decltype(hana::size(std::declval<T_derived_datasets>()) != hana::size_c<0>)::value | |||
|| decltype(hana::not_equal(std::declval<T_base_dataset>(), hana::type_c<void>))::value>> | |||
{ using type = table_polymorphic_t /* table_polymorphic_t<mp::decay_t<T_schema>, mp::decay_t<T_table>, T_base_dataset> */; }; | |||
{ using type = table_polymorphic_t<mp::decay_t<T_schema>, mp::decay_t<T_table>>; }; | |||
template< | |||
typename T_dataset, | |||
@@ -93,7 +123,6 @@ namespace mariadb { | |||
}; | |||
} | |||
} } |
@@ -1,8 +1,6 @@ | |||
#pragma once | |||
#include "table.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -10,9 +8,13 @@ namespace mariadb { | |||
/** | |||
* @brief Table for polymorphic data types. | |||
*/ | |||
template<typename T_schema, typename T_table> | |||
struct table_polymorphic_t | |||
: public table_t | |||
{ | |||
public: | |||
using dataset_type = typename T_table::dataset_type; | |||
public: | |||
/** | |||
* @brief Value constructor. Creates a mariadb table from the cpphibernate table. | |||
@@ -22,8 +24,6 @@ namespace mariadb { | |||
* @param[in] p_table Cpphibernate table to create mariadb table for. | |||
*/ | |||
template< | |||
typename T_schema, | |||
typename T_table, | |||
typename T_base_dataset, | |||
typename T_derived_datasets> | |||
inline table_polymorphic_t( | |||
@@ -32,6 +32,50 @@ namespace mariadb { | |||
const T_table& p_table, | |||
const T_base_dataset&&, | |||
const T_derived_datasets&&); | |||
public: | |||
/** | |||
* @brief Execute a create/update operation on the current table. | |||
* | |||
* For a polymorphic type this will check the derived tables before executing the actual opertion. | |||
* If the dataset matches one of the dericed tables, the operation is executed on this table. | |||
* This operation also updates the base table if the dataset has one. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
* | |||
* @return Returns the key of the created/updated dataset in it's string representation. | |||
*/ | |||
std::string create_update(const create_update_context& context) const override; | |||
/** | |||
* @brief Execute a destroy operation on the current table. | |||
* | |||
* For a polymorphic type this will check the derived tables before executing the actual operation. | |||
* If the dataset matches one of the dericed tables, the operation is executed on this table. | |||
* This operation also updates the base table if the dataset has one. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
*/ | |||
void destroy(const destroy_context& context) const override; | |||
public: | |||
/** | |||
* @brief Emplace new dataset of the type the table represents. | |||
* | |||
* @param[in] context Context to emplace new dataset in. | |||
*/ | |||
void emplace(const read_context& context) const override; | |||
private: | |||
/** | |||
* @brief Execute the predicate for each derived type. | |||
* | |||
* @param[in] dataset Dataset to try to cast to derived type. | |||
* @param[in] include_self True: Also check the dataset type. False: Only check derived types. | |||
* @param[in] pred Predicate to execute if dataset could be cast to a derived type. | |||
*/ | |||
template<typename T_dataset, typename T_pred, typename T_include_self> | |||
constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; | |||
}; | |||
} } |
@@ -2,6 +2,9 @@ | |||
#include "table_polymorphic.h" | |||
#include "../../context/create_update_context.inl" | |||
#include "../../context/destroy_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -30,25 +33,46 @@ namespace mariadb { | |||
static constexpr decltype(auto) make_dataset_id_vector = make_dataset_id_vector_impl { }; | |||
/* filter_own_type_impl */ | |||
template<typename T_dataset, typename T_include_self> | |||
struct filter_self_type_impl | |||
{ | |||
template<typename T_type> | |||
inline decltype(auto) operator() (T_type&&) const | |||
{ | |||
return hana::and_( | |||
hana::not_(hana::trait<std::is_abstract>(T_type{ })), | |||
hana::or_( | |||
T_type{ } != hana::type_c<T_dataset>, | |||
T_include_self { })); | |||
} | |||
}; | |||
template<typename T_dataset, typename T_include_self> | |||
static constexpr decltype(auto) filter_self_type = filter_self_type_impl<T_dataset, T_include_self> { }; | |||
} | |||
/* table_polymorphic_t */ | |||
template< | |||
typename T_schema, | |||
typename T_table, | |||
typename T_table> | |||
template< | |||
typename T_base_dataset, | |||
typename T_derived_datasets> | |||
table_polymorphic_t::table_polymorphic_t( | |||
const schema_t& p_owner, | |||
const T_schema& p_schema, | |||
const T_table& p_table, | |||
const T_base_dataset&&, | |||
const T_derived_datasets&&) | |||
: table_t( | |||
p_owner, | |||
p_schema, | |||
p_table) | |||
table_polymorphic_t<T_schema, T_table> | |||
::table_polymorphic_t( | |||
const schema_t& p_owner, | |||
const T_schema& p_schema, | |||
const T_table& p_table, | |||
const T_base_dataset&&, | |||
const T_derived_datasets&&) | |||
: table_t( | |||
p_owner, | |||
p_schema, | |||
p_table) | |||
{ | |||
this->base_dataset_id = hana::if_( | |||
hana::equal(T_base_dataset { }, hana::type_c<void>), | |||
@@ -57,4 +81,105 @@ namespace mariadb { | |||
this->derived_dataset_ids = __impl::make_dataset_id_vector(T_derived_datasets { }); | |||
} | |||
template< | |||
typename T_schema, | |||
typename T_table> | |||
std::string table_polymorphic_t<T_schema, T_table> | |||
::create_update(const create_update_context& context) const | |||
{ | |||
bool done = false; | |||
auto& dataset = context.get<dataset_type>(); | |||
for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ | |||
if (!done) | |||
{ | |||
using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>; | |||
auto derived_dataset_id = get_type_id(hana::type_c<derived_dataset_type>); | |||
auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); | |||
if (!derived_table) | |||
{ | |||
throw exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
<< "unable to find derived table info for dataset '" | |||
<< cppcore::type_helper<derived_dataset_type>::name() << "'!").str()); | |||
} | |||
derived_table->create_update(change_context(context, derived_dataset)); | |||
done = true; | |||
} | |||
}); | |||
return done | |||
? *this->primary_key_field->get(context) | |||
: this->create_update_exec(context); | |||
} | |||
template< | |||
typename T_schema, | |||
typename T_table> | |||
void table_polymorphic_t<T_schema, T_table> | |||
::destroy(const destroy_context& context) const | |||
{ | |||
bool done = false; | |||
auto& dataset = context.get<dataset_type>(); | |||
for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ | |||
if (!done) | |||
{ | |||
using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>; | |||
auto derived_dataset_id = get_type_id(hana::type_c<derived_dataset_type>); | |||
auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); | |||
if (!derived_table) | |||
{ | |||
throw exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
<< "unable to find derived table info for dataset '" | |||
<< cppcore::type_helper<derived_dataset_type>::name() << "'!").str()); | |||
} | |||
auto new_context = change_context(context, derived_dataset); | |||
derived_table->destroy(new_context); | |||
done = true; | |||
} | |||
}); | |||
if (!done) | |||
{ | |||
this->destroy_exec(context); | |||
} | |||
} | |||
template< | |||
typename T_schema, | |||
typename T_table> | |||
void table_polymorphic_t<T_schema, T_table> | |||
::emplace(const read_context& context) const | |||
{ | |||
hana::eval_if( | |||
std::is_abstract_v<dataset_type>, | |||
[](){ | |||
throw exception(std::string("can not create dataset of abstract type: ") | |||
+ cppcore::type_helper<dataset_type>::name()); | |||
}, | |||
[&context, this](auto _){ | |||
_(context).template emplace<dataset_type>(this); | |||
}); | |||
} | |||
template< | |||
typename T_schema, | |||
typename T_table> | |||
template< | |||
typename T_dataset, | |||
typename T_pred, | |||
typename T_include_self> | |||
constexpr void table_polymorphic_t<T_schema, T_table> | |||
::for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const | |||
{ | |||
auto derived_types = decltype(hana::filter( | |||
schema::get_all_derived_types(std::declval<T_schema>(), hana::type_c<dataset_type>), | |||
__impl::filter_self_type<mp::decay_t<T_dataset>, mp::decay_t<T_include_self>>)) { }; | |||
hana::for_each(derived_types, [&](auto& type){ | |||
using derived_type = decay_unwrap_t<decltype(type)>; | |||
auto* derived = dynamic_cast<derived_type*>(&dataset); | |||
if (derived) | |||
pred(*derived); | |||
}); | |||
} | |||
} } |
@@ -1,8 +1,6 @@ | |||
#pragma once | |||
#include "table.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include "base_context.h" | |||
namespace cpphibernate { | |||
@@ -16,6 +18,8 @@ namespace mariadb { | |||
} | |||
struct table_t; | |||
/** | |||
* @brief Predicate to change the stored dataset of any data_context. | |||
*/ | |||
@@ -35,6 +39,17 @@ namespace mariadb { | |||
template<typename X, typename T_enable> | |||
friend struct __impl::change_context_builder; | |||
protected: | |||
/** | |||
* @brief Constructor. | |||
* | |||
* @param[in] p_schema Mariadb driver schema to use for the operation. | |||
* @param[in] p_connection Mariadb connection to execute queries with. | |||
*/ | |||
inline data_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection); | |||
public: | |||
/** | |||
* @brief Constructor. | |||
@@ -56,12 +71,22 @@ namespace mariadb { | |||
template<typename T_dataset> | |||
inline decltype(auto) get() const; | |||
private: | |||
protected: | |||
/** | |||
* @brief Set the dataset that is stored in this context. | |||
* | |||
* @param[in] dataset Dataset to store in the context. | |||
* @param[in] dataset_id Unique id of the dataset stored in the context. | |||
* | |||
* @return Pointer to the stored dataset. | |||
*/ | |||
template<typename T_dataset> | |||
inline void set(T_dataset& dataset); | |||
inline void * set(T_dataset& dataset, size_t dataset_id = 0) const; | |||
/** | |||
* @brief Clear the context (set the stored dataset to nullptr). | |||
*/ | |||
inline void clear() const; | |||
}; | |||
} } |
@@ -2,6 +2,7 @@ | |||
#include "data_context.h" | |||
#include "base_context.inl" | |||
#include "../classes/schema/schema.inl" | |||
namespace cpphibernate { | |||
@@ -9,6 +10,15 @@ namespace mariadb { | |||
/* data_context */ | |||
data_context::data_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection) | |||
: base_context (p_schema, p_connection) | |||
, _dataset_id (0) | |||
, _dataset (nullptr) | |||
, _table (nullptr) | |||
{ } | |||
template<typename T_dataset> | |||
data_context::data_context( | |||
const schema_t& p_schema, | |||
@@ -60,10 +70,20 @@ namespace mariadb { | |||
} | |||
template<typename T_dataset> | |||
void data_context::set(T_dataset& dataset) | |||
void * data_context::set(T_dataset& dataset, size_t dataset_id) const | |||
{ | |||
_dataset_id = dataset_id | |||
? dataset_id | |||
: get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||
_dataset = &dataset; | |||
_table = nullptr; | |||
return _dataset; | |||
} | |||
void data_context::clear() const | |||
{ | |||
_dataset_id = get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||
_dataset = &dataset; | |||
_dataset_id = 0; | |||
_dataset = nullptr; | |||
_table = nullptr; | |||
} | |||
@@ -0,0 +1,23 @@ | |||
#pragma once | |||
#include "data_context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Mariadb driver context for deleting datasets from the database. | |||
*/ | |||
struct destroy_context | |||
: public data_context | |||
{ | |||
std::string where; //!< Where clause for deleting datasets. | |||
template<typename T_dataset> | |||
inline destroy_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset); | |||
}; | |||
} } |
@@ -0,0 +1,20 @@ | |||
#pragma once | |||
#include "destroy_context.h" | |||
#include "data_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* destroy_context */ | |||
template<typename T_dataset> | |||
destroy_context::destroy_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset) | |||
: data_context (p_schema, p_connection, p_dataset) | |||
{ } | |||
} } |
@@ -0,0 +1,104 @@ | |||
#pragma once | |||
#include "data_context.h" | |||
#include "../impl/filter.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Context that is used for read operations. | |||
*/ | |||
struct read_context | |||
: public data_context | |||
{ | |||
protected: | |||
const size_t real_dataset_id; //!< Unique id of thre real dataset the context was created with. | |||
public: | |||
const filter_t& filter; //!< Filter that is used for read operation. | |||
bool is_dynamic; //!< The dataset managed by this context is dynamic/polymorhp | |||
std::string where; //!< Where statement to use for selection | |||
std::string limit; //!< Limit statement to use for selection | |||
std::string order_by; //!< Order statement to use for selection | |||
public: | |||
/** | |||
* @brief Destructor. | |||
*/ | |||
virtual ~read_context() = default; | |||
/** | |||
* @brief Emplace a new value in the dataset of the read context. | |||
* | |||
* @tparam T_dataset Type of the dataset to emplace. | |||
* | |||
* @param[in] table Table of the dataset to emplace. | |||
* If the table is unknown, the table is searched within the schema. | |||
* | |||
* @return Reference to the emplaced dataset. | |||
*/ | |||
template<typename T_dataset> | |||
inline T_dataset& emplace(const table_t * table = nullptr) const; | |||
/** | |||
* @brief Emplace a new value in the dataset of the read context. | |||
*/ | |||
inline void emplace() const; | |||
/** | |||
* @brief Finish the emplacement of values in the dataset. | |||
*/ | |||
inline void finish() const; | |||
private: | |||
/** | |||
* @brief Actual emplace method. This method should create a new value entry in the dataset of the read context. | |||
* | |||
* @param[in] data Actual data to emplace. If nullptr is passed a default value is created. | |||
* @param[in] dataset_id Unique id of the dataset to emplace (must match the object pointed to by data). | |||
* | |||
* @return Pointer to the dataset that was emplaced. | |||
*/ | |||
virtual void * emplace_intern(void * data, size_t dataset_id) const = 0; | |||
/** | |||
* @brief Actual finish method. Should check how many element has been emplaced in the dataset. | |||
*/ | |||
virtual void finish_intern () const = 0; | |||
protected: | |||
/** | |||
* @brief Constructor. Creates a create context (no filters are passed). | |||
* | |||
* @param[in] p_schema Mariadb driver schema to use for the operation. | |||
* @param[in] p_connection Mariadb connection to execute queries with. | |||
* @param[in] p_filter Filter to use for the read operation. | |||
* @param[in] p_real_dataset_id Unique id the dataset that is stored in the context. | |||
*/ | |||
inline read_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter, | |||
size_t p_real_dataset_id); | |||
}; | |||
using read_context_ptr_u = std::unique_ptr<read_context>; | |||
namespace __impl | |||
{ | |||
/** | |||
* @brief Helper class to create read contexts. | |||
*/ | |||
template<typename X, typename = void> | |||
struct read_context_builder; | |||
} | |||
/** | |||
* @brief Predicate to create read contexts. | |||
*/ | |||
constexpr decltype(auto) make_read_context = mp::generic_predicate<__impl::read_context_builder> { }; | |||
} } |
@@ -0,0 +1,364 @@ | |||
#pragma once | |||
#include "read_context.h" | |||
#include "data_context.inl" | |||
#include "../helper/nullable_helper.inl" | |||
#include "../helper/container_helper.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
read_context::read_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter, | |||
size_t p_real_dataset_id) | |||
: data_context (p_schema, p_connection) | |||
, filter (p_filter) | |||
, real_dataset_id (p_real_dataset_id) | |||
, is_dynamic (false) | |||
{ } | |||
template<typename T_dataset> | |||
T_dataset& read_context::emplace(const table_t * table) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
/* check table */ | |||
auto dataset_id = get_type_id(hana::type_c<dataset_type>); | |||
if (!table) | |||
table = &schema.table(dataset_id); | |||
else if (table->dataset_id != dataset_id) | |||
throw exception("wrong table passed!"); | |||
/* check base */ | |||
auto tbl = table; | |||
while (tbl && tbl->dataset_id != real_dataset_id) | |||
tbl = tbl->base_table; | |||
if (!tbl) | |||
{ | |||
throw exception(cppcore::type_helper<dataset_type>::name() + | |||
" is not a derived type of dataset with id " + std::to_string(real_dataset_id)); | |||
} | |||
/* create dataset */ | |||
auto ptr = std::make_unique<dataset_type>(); | |||
auto data = emplace_intern(ptr.get(), dataset_id); | |||
if (!data) | |||
throw exception("unable to store created dataset in context!"); | |||
ptr.release(); | |||
return *static_cast<dataset_type*>(data); | |||
} | |||
void read_context::emplace() const | |||
{ emplace_intern(nullptr, 0); } | |||
void read_context::finish() const | |||
{ finish_intern(); } | |||
namespace __impl | |||
{ | |||
/* read_context_builder */ | |||
template<typename X, typename> | |||
struct read_context_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for make_read_context(...)!"); } | |||
}; | |||
/* read_context_builder - fake */ | |||
template< | |||
typename T_schema, | |||
typename T_connection, | |||
typename T_dataset, | |||
typename T_filter> | |||
struct read_context_builder< | |||
mp::list<T_schema, T_connection, T_dataset, T_filter>, | |||
mp::enable_if_t< | |||
mp::is_same_v<hana::type_tag, hana::tag_of_t<mp::decay_t<T_dataset>>> | |||
>> | |||
{ | |||
using dataset_type = typename mp::decay_t<T_dataset>::type; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
public: | |||
inline context_impl( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter) | |||
: read_context (p_schema, p_connection, p_filter, | |||
get_type_id(hana::type_c<real_dataset_t<mp::decay_t<dataset_type>>>)) | |||
{ } | |||
private: | |||
void * emplace_intern(void* data, size_t dataset_id) const override | |||
{ return nullptr; } | |||
void finish_intern() const override | |||
{ clear(); } | |||
}; | |||
template<typename... X_args> | |||
static constexpr decltype(auto) apply(X_args&&... args) | |||
{ return context_impl(std::forward<X_args>(args)...); } | |||
}; | |||
/* read_context_builder - normal types */ | |||
template< | |||
typename T_schema, | |||
typename T_connection, | |||
typename T_dataset, | |||
typename T_filter> | |||
struct read_context_builder< | |||
mp::list<T_schema, T_connection, T_dataset, T_filter>, | |||
mp::enable_if_t< | |||
!is_container_v<mp::decay_t<T_dataset>> | |||
&& !is_nullable_v<mp::decay_t<T_dataset>> | |||
&& !mp::is_same_v<hana::type_tag, hana::tag_of_t<mp::decay_t<T_dataset>>> | |||
>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
mutable size_t _count; | |||
dataset_type& _dataset; | |||
public: | |||
inline context_impl( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter) | |||
: read_context (p_schema, p_connection, p_filter, | |||
get_type_id(hana::type_c<real_dataset_t<mp::decay_t<T_dataset>>>)) | |||
, _count (0) | |||
, _dataset (p_dataset) | |||
{ } | |||
private: | |||
void * emplace_intern(void* data, size_t dataset_id) const override | |||
{ | |||
if (data || dataset_id != 0) | |||
throw exception("Static datasets can not be assigned!"); | |||
++_count; | |||
if (_count > 1) | |||
throw exception("Expected exactly one dataset, but received more!"); | |||
return set(_dataset); | |||
} | |||
void finish_intern() const override | |||
{ | |||
if (_count < 1) | |||
throw exception("Expected exactly one dataset, but received none!"); | |||
clear(); | |||
} | |||
}; | |||
template<typename... X_args> | |||
static constexpr decltype(auto) apply(X_args&&... args) | |||
{ return context_impl(std::forward<X_args>(args)...); } | |||
}; | |||
/* read_context_builder - nullable */ | |||
template< | |||
typename T_schema, | |||
typename T_connection, | |||
typename T_dataset, | |||
typename T_filter> | |||
struct read_context_builder< | |||
mp::list<T_schema, T_connection, T_dataset, T_filter>, | |||
mp::enable_if_t< | |||
!is_container_v<mp::decay_t<T_dataset>> | |||
&& is_nullable_v<mp::decay_t<T_dataset>> | |||
>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using nullable_helper_type = nullable_helper<dataset_type>; | |||
using value_type = typename nullable_helper_type::value_type; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
dataset_type& _dataset; | |||
mutable size_t _count; | |||
public: | |||
inline context_impl( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter) | |||
: read_context (p_schema, p_connection, p_filter, | |||
get_type_id(hana::type_c<real_dataset_t<mp::decay_t<T_dataset>>>)) | |||
, _count (0) | |||
, _dataset (p_dataset) | |||
{ | |||
is_dynamic = is_pointer_v<dataset_type>; | |||
nullable_helper_type::clear(_dataset); | |||
} | |||
private: | |||
void * emplace_intern(void * data, size_t dataset_id) const override | |||
{ | |||
if (data && !is_pointer_v<dataset_type>) | |||
throw exception("None pointer type can not be assigned!"); | |||
++_count; | |||
if (_count > 1) | |||
throw exception("Expected exactly one dataset, but received more!"); | |||
if (data) | |||
{ | |||
auto* cast = static_cast<value_type*>(data); | |||
auto& value = nullable_helper_type::set(_dataset, cast); | |||
if (cast != &value) | |||
throw exception("Nullable pointer value has changed!"); | |||
return set(value, dataset_id); | |||
} | |||
else | |||
{ | |||
auto& value = nullable_helper_type::set(_dataset, value_type { }); | |||
return set(value); | |||
} | |||
} | |||
void finish_intern() const override | |||
{ clear(); } | |||
}; | |||
template<typename... X_args> | |||
static constexpr decltype(auto) apply(X_args&&... args) | |||
{ return context_impl(std::forward<X_args>(args)...); } | |||
}; | |||
/* container_emplace_helper */ | |||
template<typename T_dataset, typename = void> | |||
struct container_emplace_helper; | |||
template<typename T_dataset> | |||
struct container_emplace_helper< | |||
T_dataset, | |||
mp::enable_if_t< | |||
mp::is_valid_v<typename container_helper<T_dataset>::value_type> | |||
&& !is_pointer_v<typename container_helper<T_dataset>::value_type> | |||
>> | |||
{ | |||
static inline decltype(auto) emplace(T_dataset& dataset, void * data, size_t& data_id) | |||
{ | |||
using container_helper_type = container_helper<T_dataset>; | |||
using value_type = typename container_helper_type::value_type; | |||
if (data || data_id != 0) | |||
throw exception("Static datasets can not be assigned!"); | |||
auto& value = container_helper_type::emplace(dataset); | |||
data_id = get_type_id(hana::type_c<value_type>); | |||
return value; | |||
} | |||
}; | |||
template<typename T_dataset> | |||
struct container_emplace_helper< | |||
T_dataset, | |||
mp::enable_if_t< | |||
mp::is_valid_v<typename container_helper<T_dataset>::value_type> | |||
&& is_pointer_v<typename container_helper<T_dataset>::value_type> | |||
>> | |||
{ | |||
static inline decltype(auto) emplace(T_dataset& dataset, void * data, size_t& data_id) | |||
{ | |||
using container_helper_type = container_helper<T_dataset>; | |||
using nullable_type = typename container_helper_type::value_type; | |||
using nullable_helper_type = nullable_helper<nullable_type>; | |||
using inner_value_type = typename nullable_helper_type::value_type; | |||
if (!data) | |||
throw exception("Expected dynamic data for pointer type!"); | |||
if (data_id == 0) | |||
throw exception("Expected dataset id for pointer type!"); | |||
if (!is_pointer_v<nullable_type>) | |||
throw exception("None pointer type can not be assigned!"); | |||
auto& nullable = container_helper_type::emplace(dataset); | |||
auto* cast = static_cast<inner_value_type*>(data); | |||
auto& value = nullable_helper_type::set(nullable, cast); | |||
if (cast != &value) | |||
throw exception("Nullable pointer value has changed!"); | |||
return value; | |||
} | |||
}; | |||
/* read_context_builder - container */ | |||
template< | |||
typename T_schema, | |||
typename T_connection, | |||
typename T_dataset, | |||
typename T_filter> | |||
struct read_context_builder< | |||
mp::list<T_schema, T_connection, T_dataset, T_filter>, | |||
mp::enable_if_t< | |||
is_container_v<mp::decay_t<T_dataset>> | |||
&& !is_nullable_v<mp::decay_t<T_dataset>> | |||
>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = real_dataset_t<dataset_type>; | |||
using container_helper_type = container_helper<dataset_type>; | |||
using value_type = typename container_helper_type::value_type; | |||
using emplace_helper_type = container_emplace_helper<dataset_type>; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
dataset_type& _dataset; | |||
public: | |||
inline context_impl( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter) | |||
: read_context (p_schema, p_connection, p_filter, | |||
get_type_id(hana::type_c<real_dataset_t<mp::decay_t<T_dataset>>>)) | |||
, _dataset (p_dataset) | |||
{ | |||
is_dynamic = is_pointer_v<value_type>; | |||
container_helper_type::clear(_dataset); | |||
} | |||
private: | |||
void * emplace_intern(void * data, size_t dataset_id) const override | |||
{ | |||
auto& value = emplace_helper_type::emplace(_dataset, data, dataset_id); | |||
return set(value, dataset_id); | |||
} | |||
void finish_intern() const override | |||
{ clear(); } | |||
}; | |||
template<typename... X_args> | |||
static constexpr decltype(auto) apply(X_args&&... args) | |||
{ return context_impl(std::forward<X_args>(args)...); } | |||
}; | |||
} | |||
} } |
@@ -13,9 +13,13 @@ namespace mariadb { | |||
*/ | |||
struct driver_t | |||
{ | |||
public: | |||
friend struct driver_impl_t; | |||
private: | |||
driver_impl_t _impl; //!< Driver implementation. | |||
::cppmariadb::connection * _connection; //!< Mariadb connection to use for queries. | |||
filter_t _filter; //!< Filter to use for read and update operations. | |||
public: | |||
/** | |||
@@ -51,116 +55,32 @@ namespace mariadb { | |||
*/ | |||
inline void connection(::cppmariadb::connection * p_connection); | |||
protected: | |||
/** | |||
* @brief Get the imlementation object of the driver. | |||
* @brief Set the include filter to use for update and read operations. | |||
* | |||
* @param[in] args Fileds and tables to use as include filter. | |||
*/ | |||
inline auto& impl() const; | |||
/* | |||
public: | |||
using lock_type = std::unique_ptr<transaction_lock>; | |||
private: | |||
::cppmariadb::connection* _connection; | |||
schema_t _schema; | |||
filter_t _filter; | |||
public: | |||
template<typename T_schema> | |||
mariadb_driver_t(T_schema&& p_schema) | |||
: _schema(make_schema(std::forward<T_schema>(p_schema))) | |||
{ } | |||
cpphibernate_copyable(mariadb_driver_t, delete); | |||
cpphibernate_moveable(mariadb_driver_t, default); | |||
inline const ::cppmariadb::connection& connection() const | |||
{ return *_connection; } | |||
inline void connection(::cppmariadb::connection& p_connection) | |||
{ _connection = &p_connection; } | |||
template<typename... T_args> | |||
inline void set_filter_inclusive(T_args&&... args) | |||
{ _filter.set_inclusive(_schema, std::forward<T_args>(args)...); } | |||
inline void set_filter_inclusive(T_args&&... args); | |||
/** | |||
* @brief Set the exclude filter to use for update and read operations. | |||
* | |||
* @param[in] args Fileds and tables to use as exclude filter. | |||
*/ | |||
template<typename... T_args> | |||
inline void set_filter_exclusive(T_args&&... args) | |||
{ _filter.set_exclusive(_schema, std::forward<T_args>(args)...); } | |||
inline void clear_filter() | |||
{ _filter.clear(); } | |||
inline lock_type lock() | |||
{ return std::make_unique<transaction_lock>(*_connection); } | |||
inline void set_filter_exclusive(T_args&&... args); | |||
template<typename T_modifiers> | |||
inline std::string build_query(const std::string& query, T_modifiers&& modifiers) const | |||
{ | |||
auto where = build_where(_schema, modifiers).query(*_connection); | |||
std::ostringstream os; | |||
os << query; | |||
if (!where.empty()) | |||
os << " " << where; | |||
return os.str(); | |||
} | |||
/** | |||
* @brief Clear the filter. | |||
*/ | |||
inline void clear_filter(); | |||
protected: | |||
inline void init_impl(bool recreate) const | |||
{ | |||
transaction_lock trans(*_connection); | |||
_schema.init(init_context(_schema, *_connection, recreate)); | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
inline void create_impl(T_dataset& dataset) const | |||
{ | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, *_connection, _filter, false)); | |||
} | |||
template<typename T_dataset, typename T_modifiers> | |||
inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = _schema.table(dataset_id); | |||
auto context = make_read_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); | |||
transaction_lock trans(*_connection); | |||
table.read(context); | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
inline void update_impl(T_dataset& dataset) const | |||
{ | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, *_connection, _filter, true)); | |||
} | |||
template<typename T_dataset> | |||
inline void destroy_impl(T_dataset& dataset) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = _schema.table(dataset_id); | |||
destroy_context context(dataset, _schema, *_connection); | |||
context.where = table.get_where_primary_key(context); | |||
destroy_impl_t<T_dataset>::apply(context); | |||
} | |||
*/ | |||
/** | |||
* @brief Get the imlementation object of the driver. | |||
*/ | |||
inline auto& impl() const; | |||
}; | |||
} } |
@@ -27,6 +27,17 @@ namespace mariadb { | |||
void driver_t::connection(::cppmariadb::connection * p_connection) | |||
{ _connection = p_connection; } | |||
template<typename... T_args> | |||
void driver_t::set_filter_inclusive(T_args&&... args) | |||
{ _filter.set_inclusive(_impl.schema, std::forward<T_args>(args)...); } | |||
template<typename... T_args> | |||
void driver_t::set_filter_exclusive(T_args&&... args) | |||
{ _filter.set_exclusive(_impl.schema, std::forward<T_args>(args)...); } | |||
void driver_t::clear_filter() | |||
{ _filter.clear(); } | |||
auto& driver_t::impl() const | |||
{ return _impl; } | |||
@@ -0,0 +1,27 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Helper class to manage operations on nullable types. | |||
*/ | |||
template<typename T_container, typename = void> | |||
struct container_helper | |||
{ | |||
using container_type = T_container; | |||
using value_type = typename container_type::value_type; | |||
/** | |||
* @brief Emplace a new dataset in the container. | |||
*/ | |||
template<typename... X_args> | |||
static inline value_type& emplace(container_type& container, X_args&&... args) = delete; | |||
/** | |||
* @brief Clear the whole container. | |||
*/ | |||
static inline void clear(container_type& container) = delete; | |||
}; | |||
} } |
@@ -0,0 +1,43 @@ | |||
#pragma once | |||
#include <list> | |||
#include <vector> | |||
#include "container_helper.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* container_helper */ | |||
template<typename T_container> | |||
struct container_helper< | |||
T_container, | |||
mp::enable_if_t< | |||
mp::is_specialization_of_v<mp::decay_t<T_container>, std::list> | |||
|| mp::is_specialization_of_v<mp::decay_t<T_container>, std::vector> | |||
>> | |||
{ | |||
using container_type = T_container; | |||
using value_type = typename container_type::value_type; | |||
/** | |||
* @brief Emplace a new dataset in the container. | |||
*/ | |||
template<typename... X_args> | |||
static inline value_type& emplace(container_type& container, X_args&&... args) | |||
{ | |||
container.emplace_back(std::forward<X_args>(args)...); | |||
return container.back(); | |||
} | |||
/** | |||
* @brief Clear the whole container. | |||
*/ | |||
static inline void clear(container_type& container) | |||
{ | |||
container.clear(); | |||
} | |||
}; | |||
} } |
@@ -1,8 +1,6 @@ | |||
#pragma once | |||
#include "key_properties.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,7 +1,5 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,8 +1,6 @@ | |||
#pragma once | |||
#include "nullable_helper.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -41,8 +39,8 @@ namespace mariadb { | |||
struct nullable_helper< | |||
T, | |||
mp::enable_if_t< | |||
mp::is_specialization_of_v<T, std::unique_ptr> | |||
|| mp::is_specialization_of_v<T, std::shared_ptr>>> | |||
mp::is_specialization_of_v<mp::decay_t<T>, std::unique_ptr> | |||
|| mp::is_specialization_of_v<mp::decay_t<T>, std::shared_ptr>>> | |||
{ | |||
using nullable_type = T; | |||
using value_type = typename nullable_type::element_type; | |||
@@ -1,9 +1,11 @@ | |||
#pragma once | |||
#include "impl/create_update.h" | |||
#include "impl/destroy.h" | |||
#include "impl/driver_impl.h" | |||
#include "impl/filter.h" | |||
#include "impl/create_update.inl" | |||
#include "impl/destroy.inl" | |||
#include "impl/driver_impl.inl" | |||
#include "impl/filter.inl" |
@@ -1,234 +0,0 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Base class for all mariadb driver context classes. | |||
*/ | |||
struct base_context | |||
{ | |||
const schema_t& schema; //!< schema to use for the operation | |||
::cppmariadb::connection& connection; //!< mariadb connection to use for executing queries | |||
/** | |||
* @brief Constructor. | |||
* | |||
* @param[in] p_schema Mariadb driver schema to use for the operation. | |||
* @param[in] p_connection Mariadb connection to execute queries with. | |||
*/ | |||
inline base_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection); | |||
}; | |||
/** | |||
* @brief Mariadb driver context for initializing the database. | |||
*/ | |||
struct init_context | |||
: public base_context | |||
{ | |||
bool recreate; //!< Drop existing tables before createing new once. | |||
/** | |||
* @brief Constructor. | |||
* | |||
* @param[in] p_schema Mariadb driver schema to use for the operation. | |||
* @param[in] p_connection Mariadb connection to execute queries with. | |||
* @param[in] p_recreate Drop existing tables before createing new once. | |||
*/ | |||
inline init_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
bool p_recreate); | |||
}; | |||
/** | |||
* @brief Mariadb driver context that helds a specific dataset. | |||
*/ | |||
struct data_context | |||
: public base_context | |||
{ | |||
private: | |||
mutable size_t _dataset_id; //!< Unique type id of the dataset that is stored in this context. | |||
mutable void* _dataset; //!< Pointer to the stored dataset. | |||
mutable const table_t* _table; //!< Table this context/dataset belongs to | |||
friend __impl::change_context_builder; | |||
public: | |||
/** | |||
* @brief Constructor. | |||
* | |||
* @param[in] p_schema Mariadb driver schema to use for the operation. | |||
* @param[in] p_connection Mariadb connection to execute queries with. | |||
* @param[in] p_dataset Dataset to store in the context. | |||
*/ | |||
template<typename T_dataset> | |||
inline data_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset); | |||
/** | |||
* @brief Get a reference to the stored dataset if the type matches. | |||
* If an invalid type is requested an exception is thrown. | |||
*/ | |||
template<typename T_dataset> | |||
inline decltype(auto) get() const; | |||
private: | |||
/** | |||
* @brief Set the dataset that is stored in this context. | |||
*/ | |||
template<typename T_dataset> | |||
inline void set(T_dataset& dataset); | |||
}; | |||
namespace __impl | |||
{ | |||
/** | |||
* @brief Helper class to create the change_context predicate. | |||
*/ | |||
template<typename X, typename = void> | |||
struct change_context_builder; | |||
} | |||
/** | |||
* @brief Change the stored dataset of any data_context. | |||
* | |||
* @param[in] context Any data_context. | |||
* @param[in] dataset New dataset of the context. | |||
* | |||
* @return The new conext that stores the passed dataset. | |||
*/ | |||
constexpr decltype(auto) change_context = cppmp::generic_predicate<__impl::change_context_builder> { }; | |||
#if 0 | |||
/* filter_context */ | |||
struct filter_context | |||
: public data_context | |||
{ | |||
const filter_t& filter; | |||
inline filter_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter) | |||
: data_context (p_schema, p_connection) | |||
, filter (p_filter) | |||
{ } | |||
template<typename T_data> | |||
inline filter_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter) | |||
: data_context (p_data, p_schema, p_connection) | |||
, filter (p_filter) | |||
{ } | |||
}; | |||
/* create_update_context */ | |||
struct create_update_context | |||
: public filter_context | |||
{ | |||
bool is_update; | |||
const table_t* derived_table; | |||
const field_t* owner_field; | |||
std::string owner_key; | |||
ssize_t index; | |||
template<typename T_data> | |||
inline create_update_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter, | |||
bool p_is_update) | |||
: filter_context(p_data, p_schema, p_connection, p_filter) | |||
, is_update (p_is_update) | |||
, derived_table (nullptr) | |||
, owner_field (nullptr) | |||
, index (-1) | |||
{ } | |||
}; | |||
/* read_context */ | |||
struct read_context | |||
: public filter_context | |||
{ | |||
protected: | |||
size_t base_dataset_id; | |||
public: | |||
bool is_dynamic; | |||
std::string where; | |||
std::string limit; | |||
std::string order_by; | |||
protected: | |||
inline read_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
const filter_t& p_filter) | |||
: filter_context (p_schema, p_connection, p_filter) | |||
, is_dynamic (false) | |||
, base_dataset_id (0) | |||
{ } | |||
public: | |||
virtual ~read_context() = default; | |||
template<typename T_dataset> | |||
inline T_dataset& emplace(const table_t* table = nullptr) const; | |||
void emplace() const | |||
{ emplace_intern(nullptr, 0); } | |||
void finish() const | |||
{ finish_intern(); } | |||
private: | |||
virtual void* emplace_intern(void* data, size_t dataset_id) const = 0; | |||
virtual void finish_intern () const = 0; | |||
}; | |||
using read_context_ptr = std::unique_ptr<read_context>; | |||
/* destroy_context */ | |||
struct destroy_context | |||
: public data_context | |||
{ | |||
std::string where; | |||
template<typename T_data> | |||
inline destroy_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection) | |||
: data_context( | |||
p_data, | |||
p_schema, | |||
p_connection) | |||
{ } | |||
}; | |||
#endif | |||
} } |
@@ -1,144 +0,0 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* base_context */ | |||
base_context::base_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection) | |||
: schema (p_schema) | |||
, connection(p_connection) | |||
{ } | |||
/* data_context */ | |||
template<typename T_dataset> | |||
data_context::data_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset) | |||
: base_context (p_schema, p_connection) | |||
, _dataset_id (0) | |||
, _dataset (nullptr) | |||
, _table (nullptr) | |||
{ set(p_dataset); } | |||
template<typename T_dataset> | |||
decltype(auto) data_context::get() const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
/* check if tha context has a dataset assigned (should never fail) */ | |||
if (!_dataset) | |||
throw misc::hibernate_exception("no data assigned!"); | |||
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
/* if the dataset IDs does not match, search in the base tables for a matching ID */ | |||
if (dataset_id != _dataset_id) | |||
{ | |||
/* get the table of the stored dataset */ | |||
if (!_table) | |||
_table = &schema.table(_dataset_id); | |||
/* check if the table matches the stored dataset (should never happen) */ | |||
else if (_table->dataset_id != _dataset_id) | |||
throw misc::hibernate_exception("invalid table!"); | |||
/* walk through base tables until we have found a matching ID */ | |||
auto table = _table; | |||
while(table && table->dataset_id != dataset_id) | |||
table = table->base_table; | |||
/* check if we have found a suitable table, if not throw an exception */ | |||
if (!table) | |||
{ | |||
throw misc::hibernate_exception(utl::type_helper<dataset_type>::name() + | |||
" is not a derived type of dataset with id " + std::to_string(_dataset_id)); | |||
} | |||
} | |||
/* return the dataset */ | |||
return *static_cast<dataset_type*>(_dataset); | |||
} | |||
template<typename T_dataset> | |||
void data_context::set(T_dataset& dataset) | |||
{ | |||
_dataset_id = misc::get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||
_dataset = &dataset; | |||
_table = nullptr; | |||
} | |||
namespace __impl | |||
{ | |||
/* change_context_builder */ | |||
template<typename X, typename> | |||
struct change_context_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&...) | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for change_context(...)!"); } | |||
}; | |||
template<typename T_context, typename T_dataset> | |||
struct change_context_builder< | |||
mp::list<T_context, T_dataset>, | |||
mp::enable_if_t< | |||
mp::is_base_of_v<data_context, mp::decay_t<T_context>>>> | |||
{ | |||
static constexpr decltype(auto) apply(const T_context& context, T_dataset& dataset) | |||
{ | |||
auto new_context = context; | |||
new_context.set(dataset); | |||
return new_context; | |||
} | |||
}; | |||
} | |||
#if 0 | |||
/* read_context */ | |||
template<typename T_dataset> | |||
T_dataset& read_context | |||
::emplace(const table_t* table) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
// check table | |||
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
if (!table) | |||
table = &schema.table(dataset_id); | |||
else if (table->dataset_id != dataset_id) | |||
throw misc::hibernate_exception("wrong table passed!"); | |||
// 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<dataset_type>::name() + | |||
" is not a derived type of dataset with id " + std::to_string(base_dataset_id)); | |||
} | |||
// create dataset | |||
auto ptr = std::make_unique<dataset_type>(); | |||
auto data = emplace_intern(ptr.get(), dataset_id); | |||
if (!data) | |||
throw misc::hibernate_exception("unable to store created dataset in context!"); | |||
ptr.release(); | |||
return *static_cast<dataset_type*>(data); | |||
} | |||
#endif | |||
} } |
@@ -1,265 +0,0 @@ | |||
#pragma once | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
namespace __impl | |||
{ | |||
/* declaration */ | |||
template<typename T, typename = void> | |||
struct make_read_context_impl | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); } | |||
}; | |||
/* normal datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
mp::enable_if_c< | |||
!misc::is_container<mp::decay_t<T_dataset>>::value | |||
&& !misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
mutable size_t _count; | |||
dataset_type& _dataset; | |||
public: | |||
template<typename... X_args> | |||
context_impl(dataset_type& dataset, X_args&&... args) | |||
: read_context (std::forward<X_args>(args)...) | |||
, _count (0) | |||
, _dataset (dataset) | |||
{ | |||
is_dynamic = false; | |||
base_dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
} | |||
private: | |||
virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||
{ | |||
if (data || dataset_id != 0) | |||
throw misc::hibernate_exception("Static datasets can not be assigned!"); | |||
++_count; | |||
if (_count > 1) | |||
throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||
return set(_dataset); | |||
} | |||
virtual void finish_intern() const override | |||
{ | |||
if (_count < 1) | |||
throw misc::hibernate_exception("Expected exactly one dataset, but received none!"); | |||
} | |||
}; | |||
static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||
{ return context_impl(dataset, std::forward<T_args>(args)...); } | |||
}; | |||
/* nullable datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
mp::enable_if_c< | |||
!misc::is_container<mp::decay_t<T_dataset>>::value | |||
&& misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||
using value_type = typename nullable_helper_type::value_type; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
dataset_type& _dataset; | |||
mutable size_t _count; | |||
public: | |||
template<typename... X_args> | |||
context_impl(dataset_type& dataset, X_args&&... args) | |||
: read_context (std::forward<X_args>(args)...) | |||
, _dataset (dataset) | |||
, _count (0) | |||
{ | |||
is_dynamic = misc::is_pointer<dataset_type>::value; | |||
base_dataset_id = misc::get_type_id(hana::type_c<value_type>); | |||
nullable_helper_type::clear(_dataset); | |||
} | |||
private: | |||
virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||
{ | |||
if (data && !misc::is_pointer<dataset_type>::value) | |||
throw misc::hibernate_exception("None pointer type can not be assigned!"); | |||
++_count; | |||
if (_count > 1) | |||
throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||
if (data) | |||
{ | |||
auto* cast = static_cast<value_type*>(data); | |||
auto& value = nullable_helper_type::set(_dataset, cast); | |||
if (cast != &value) | |||
throw misc::hibernate_exception("Nullable pointer value has changed!"); | |||
return set(value, dataset_id); | |||
} | |||
else | |||
{ | |||
auto& value = nullable_helper_type::set(_dataset, value_type { }); | |||
return set(value); | |||
} | |||
} | |||
virtual void finish_intern() const override | |||
{ } | |||
}; | |||
static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||
{ return context_impl(dataset, std::forward<T_args>(args)...); } | |||
}; | |||
/* container datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
mp::enable_if_c< | |||
misc::is_container<mp::decay_t<T_dataset>>::value | |||
&& !misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
using container_helper_type = misc::container_helper<dataset_type>; | |||
using value_type = typename container_helper_type::value_type; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
private: | |||
dataset_type& _dataset; | |||
mutable size_t _count; | |||
public: | |||
template<typename... X_args> | |||
context_impl(dataset_type& dataset, X_args&&... args) | |||
: read_context (std::forward<X_args>(args)...) | |||
, _dataset (dataset) | |||
, _count (0) | |||
{ | |||
is_dynamic = misc::is_pointer<value_type>::value; | |||
base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
container_helper_type::clear(_dataset); | |||
} | |||
private: | |||
virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||
{ | |||
return hana::eval_if( | |||
misc::is_nullable<value_type> { }, | |||
[this, &data, &dataset_id](auto _){ | |||
using nullable_type = typename mp::decay_t<decltype(_(hana::type_c<value_type>))>::type; | |||
using nullable_helper_type = misc::nullable_helper<nullable_type>; | |||
using inner_value_type = typename nullable_helper_type::value_type; | |||
if (!data) | |||
throw misc::hibernate_exception("Expected dynamic data for pointer type!"); | |||
if (!misc::is_pointer<nullable_type>::value) | |||
throw misc::hibernate_exception("None pointer type can not be assigned!"); | |||
auto& nullable = container_helper_type::emplace(this->_dataset); | |||
auto* cast = static_cast<inner_value_type*>(data); | |||
auto& value = nullable_helper_type::set(nullable, cast); | |||
if (cast != &value) | |||
throw misc::hibernate_exception("Nullable pointer value has changed!"); | |||
return set(value, dataset_id); | |||
}, | |||
[this, &data, &dataset_id](){ | |||
if (data || dataset_id != 0) | |||
throw misc::hibernate_exception("Static datasets can not be assigned!"); | |||
auto& value = container_helper_type::emplace(this->_dataset); | |||
return this->set(value); | |||
}); | |||
} | |||
virtual void finish_intern() const override | |||
{ } | |||
}; | |||
static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||
{ return context_impl(dataset, std::forward<T_args>(args)...); } | |||
}; | |||
} | |||
constexpr decltype(auto) make_read_context = misc::make_generic_predicate<__impl::make_read_context_impl> { }; | |||
namespace __impl | |||
{ | |||
template<typename T, typename = void> | |||
struct make_fake_context_impl | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fake_context(...)!"); } | |||
}; | |||
template<typename T_wrapped_dataset, typename... T_args> | |||
struct make_fake_context_impl< | |||
mp::list<T_wrapped_dataset, T_args...>, | |||
mp::enable_if_c< | |||
hana::is_a_t<hana::type_tag, T_wrapped_dataset>::value>> | |||
{ | |||
using wrapped_dataset_type = mp::decay_t<T_wrapped_dataset>; | |||
using dataset_type = misc::unwrap_t<wrapped_dataset_type>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
struct context_impl | |||
: public read_context | |||
{ | |||
public: | |||
template<typename... X_args> | |||
context_impl(X_args&&... args) | |||
: read_context (std::forward<X_args>(args)...) | |||
{ | |||
is_dynamic = misc::is_pointer<dataset_type>::value; | |||
base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
} | |||
private: | |||
virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||
{ return nullptr; } | |||
virtual void finish_intern() const override | |||
{ } | |||
}; | |||
static constexpr decltype(auto) apply(T_wrapped_dataset&&, T_args&&... args) | |||
{ return context_impl(std::forward<T_args>(args)...); } | |||
}; | |||
} | |||
constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { }; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -1,7 +1,10 @@ | |||
#pragma once | |||
#include "create_update.h" | |||
#include "../classes/schema/schema.inl" | |||
#include "../helper/nullable_helper.inl" | |||
#include "../context/create_update_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -34,7 +37,9 @@ namespace mariadb { | |||
if (!strict) | |||
{ | |||
static const filter_t dummy; | |||
ret = table.create_update(context.make_update_context(dummy)); | |||
ret = table.create_update(context.is_create() | |||
? context.make_update_context(dummy) | |||
: context.make_create_context()); | |||
} | |||
/* if we expect an update operation in strict mode throw an exception | |||
@@ -0,0 +1,14 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Helper class to select the right implementation of the destroy operation for the passed datatype. | |||
* | |||
* @tparam T_dataset Dataset type to select implementation for. | |||
*/ | |||
template<typename T_dataset, typename = void> | |||
struct destroy_impl_t; | |||
} } |
@@ -1,37 +1,54 @@ | |||
#pragma once | |||
#include "destroy.h" | |||
#include "../context/destroy_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* destroy_impl_t - default */ | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* destroy_impl_t */ | |||
template<typename T_dataset, typename = void> | |||
template<typename T_dataset, typename> | |||
struct destroy_impl_t | |||
{ | |||
using dataset_type = T_dataset; | |||
static inline void apply(const destroy_context& context, bool strict = true) | |||
{ | |||
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
auto dataset_id = get_type_id(hana::type_c<dataset_type>); | |||
auto& connection = context.connection; | |||
auto& schema = context.schema; | |||
auto& table = schema.table(dataset_id); | |||
assert(table.primary_key_field); | |||
transaction_lock trans(connection); | |||
::cppmariadb::transaction_lock trans(connection); | |||
if (!table.primary_key_field->is_default(context)) | |||
{ | |||
table.destroy(context); | |||
auto& field = *table.primary_key_field; | |||
auto primary_key = field.get(context); | |||
assert(primary_key.has_value()); | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< field.table.name | |||
<< "`.`" | |||
<< field.name | |||
<< "`=" | |||
<< field.convert_to_open | |||
<< "'" | |||
<< connection.escape(*primary_key) | |||
<< "'" | |||
<< field.convert_to_close; | |||
auto new_context = context; | |||
new_context.where = os.str(); | |||
table.destroy(new_context); | |||
} | |||
else if (strict) | |||
{ | |||
throw misc::hibernate_exception("can not destroy dataset with no primary key assigned!"); | |||
throw exception("can not destroy dataset with no primary key assigned!"); | |||
} | |||
trans.commit(); | |||
} | |||
@@ -42,10 +59,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_dataset> | |||
struct destroy_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_nullable<T_dataset>>> | |||
mp::enable_if_t<is_nullable_v<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||
using nullable_helper_type = nullable_helper<dataset_type>; | |||
static inline void apply(const destroy_context& context, bool strict = true) | |||
{ | |||
@@ -61,7 +78,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
else if (strict) | |||
{ | |||
throw misc::hibernate_exception("can not destroy nullable type with no value!"); | |||
throw exception("can not destroy nullable type with no value!"); | |||
} | |||
} | |||
}; | |||
@@ -71,7 +88,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_dataset> | |||
struct destroy_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_container<T_dataset>>> | |||
mp::enable_if_t<is_container_v<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
@@ -80,7 +97,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
auto& connection = context.connection; | |||
auto& dataset = context.get<dataset_type>(); | |||
transaction_lock trans(connection); | |||
::cppmariadb::transaction_lock trans(connection); | |||
for (auto& x : dataset) | |||
{ | |||
using new_dataset_type = mp::decay_t<decltype(x)>; | |||
@@ -92,5 +109,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -38,69 +38,35 @@ namespace mariadb { | |||
* @brief Create a new dataset in the database. | |||
* This will update the primary key field of the passed dataset. | |||
* | |||
* @param[in] dataset Dataset to create in database. | |||
* @param[in] dataset Dataset to create in the database. | |||
*/ | |||
template<typename T_dataset> | |||
inline void create(T_dataset& dataset) const; | |||
/* | |||
} | |||
protected: | |||
inline void init_impl(bool recreate) const | |||
{ | |||
transaction_lock trans(*_connection); | |||
_schema.init(init_context(_schema, *_connection, recreate)); | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
inline void create_impl(T_dataset& dataset) const | |||
{ | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, *_connection, _filter, false)); | |||
} | |||
/** | |||
* @brief Read datasets from the database. | |||
* | |||
* @param[in] dataset Dataset to read from the database. | |||
* @param[in] modifiers Modifiers to apply to the select query. | |||
*/ | |||
template<typename T_dataset, typename T_modifiers> | |||
inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = _schema.table(dataset_id); | |||
auto context = make_read_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); | |||
transaction_lock trans(*_connection); | |||
table.read(context); | |||
trans.commit(); | |||
} | |||
inline void read(T_dataset& dataset, const T_modifiers& modifiers) const; | |||
/** | |||
* @brief Update an exsisting dataset in the database. | |||
* | |||
* @param[in] dataset Dataset to update in the database. | |||
*/ | |||
template<typename T_dataset> | |||
inline void update_impl(T_dataset& dataset) const | |||
{ | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, *_connection, _filter, true)); | |||
} | |||
inline void update(T_dataset& dataset) const; | |||
/** | |||
* @brief Destroy an exsisting dataset in the database. | |||
* | |||
* @param[in] dataset Dataset to destroy in the database. | |||
*/ | |||
template<typename T_dataset> | |||
inline void destroy_impl(T_dataset& dataset) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = _schema.table(dataset_id); | |||
destroy_context context(dataset, _schema, *_connection); | |||
context.where = table.get_where_primary_key(context); | |||
destroy_impl_t<T_dataset>::apply(context); | |||
} | |||
*/ | |||
inline void destroy(T_dataset& dataset) const; | |||
}; | |||
} } |
@@ -1,8 +1,13 @@ | |||
#pragma once | |||
#include "../context/init_context.inl" | |||
#include "../context/create_update_context.inl" | |||
#include "../context/read_context.inl" | |||
#include "../context/destroy_context.inl" | |||
#include "driver_impl/where.inl" | |||
#include "driver_impl/limit.inl" | |||
#include "driver_impl/order_by.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,4 +40,47 @@ namespace mariadb { | |||
create_update_context(*schema, *connection, dataset)); | |||
} | |||
template<typename T_dataset, typename T_modifiers> | |||
inline void driver_impl_t::read(T_dataset& dataset, const T_modifiers& modifiers) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = real_dataset_t<dataset_type>; | |||
auto * connection = owner.connection(); | |||
if (!connection) | |||
throw exception("Cpphibernate mariadb driver is not connected to any database!"); | |||
auto dataset_id = get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = schema->table(dataset_id); | |||
auto context = make_read_context(*schema, *connection, dataset, owner._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); | |||
::cppmariadb::transaction_lock trans(*connection); | |||
table.read(context); | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
void driver_impl_t::update(T_dataset& dataset) const | |||
{ | |||
auto * connection = owner.connection(); | |||
if (!connection) | |||
throw exception("Cpphibernate mariadb driver is not connected to any database!"); | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(*schema, *connection, dataset, owner._filter)); | |||
} | |||
template<typename T_dataset> | |||
void driver_impl_t::destroy(T_dataset& dataset) const | |||
{ | |||
auto * connection = owner.connection(); | |||
if (!connection) | |||
throw exception("Cpphibernate mariadb driver is not connected to any database!"); | |||
destroy_impl_t<T_dataset>::apply( | |||
destroy_context(*schema, *connection, dataset)); | |||
} | |||
} } |
@@ -1,11 +1,10 @@ | |||
#pragma once | |||
#include <cpphibernate/modifier/limit.h> | |||
#include <cpphibernate/modifier/offset.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* limit_builder */ | |||
@@ -21,18 +20,18 @@ beg_namespace_cpphibernate_driver_mariadb | |||
hana::for_each(p_modifier, [&limit, &offset](auto& x_modifier){ | |||
using modifier_type = mp::decay_t<decltype(x_modifier)>; | |||
using is_limit_type = modifier::is_limit_modifier<modifier_type>; | |||
using is_offset_type = modifier::is_offset<modifier_type>; | |||
using is_limit_type = is_limit_modifier<modifier_type>; | |||
using is_offset_type = is_offset_modifier<modifier_type>; | |||
hana::eval_if( | |||
is_limit_type { }, | |||
[&limit, &x_modifier](auto _){ | |||
limit = static_cast<ssize_t>(hana::value(_(x_modifier).value)); | |||
limit = static_cast<ssize_t>(hana::value(_(x_modifier))); | |||
}, | |||
[&offset, &x_modifier](){ | |||
hana::eval_if( | |||
is_offset_type { }, | |||
[&offset, &x_modifier](auto _){ | |||
offset = static_cast<ssize_t>(hana::value(_(x_modifier).value)); | |||
offset = static_cast<ssize_t>(hana::value(_(x_modifier))); | |||
}, | |||
[]{ | |||
/* no-op */ | |||
@@ -61,5 +60,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
return builder.statement; | |||
} | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -1,10 +1,7 @@ | |||
#pragma once | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
namespace __impl | |||
{ | |||
@@ -32,7 +29,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
constexpr decltype(auto) make_modifier_tag = misc::make_generic_predicate<__impl::make_modifier_tag_impl> { }; | |||
constexpr decltype(auto) make_modifier_tag = mp::generic_predicate<__impl::make_modifier_tag_impl> { }; | |||
namespace __impl | |||
{ | |||
@@ -63,7 +60,6 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
constexpr decltype(auto) make_modifier_tags = misc::make_generic_predicate<__impl::make_modifier_tags_impl> { }; | |||
constexpr decltype(auto) make_modifier_tags = mp::generic_predicate<__impl::make_modifier_tags_impl> { }; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -1,12 +1,11 @@ | |||
#pragma once | |||
#include <cpphibernate/modifier/order_by.h> | |||
#include <cpphibernate/modifier/order_by/ascending.h> | |||
#include <cpphibernate/modifier/order_by/descending.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* order_by_builder */ | |||
@@ -30,20 +29,20 @@ 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_order_by_type = modifier::is_order_by<modifier_type>; | |||
using is_order_by_type = is_order_by_modifier<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>; | |||
hana::for_each(_(modifier).order_directions, [&](auto& dir){ | |||
using order_direction_type = mp::decay_t<decltype(dir)>; | |||
using is_ascencing_type = is_ascending<order_direction_type>; | |||
if (index++ == 0) os << "ORDER_BY "; | |||
else os << ", "; | |||
auto& field = schema.field(misc::get_type_id(f.wrapped_field)); | |||
auto& field = schema.field(get_type_id(dir.wrapped_field)); | |||
os << "`" | |||
<< field.table_name | |||
<< field.table.name | |||
<< "`.`" | |||
<< field.field_name | |||
<< field.name | |||
<< "` " | |||
<< (is_ascencing_type::value | |||
? "ASC" | |||
@@ -79,5 +78,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
return builder.assign(schema, modifiers); | |||
} | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -1,12 +1,13 @@ | |||
#pragma once | |||
#include <cpphibernate/modifier/where.h> | |||
#include <cpphibernate/modifier/where/equal.h> | |||
#include <cpphibernate/modifier/where/negation.h> | |||
#include <cpphibernate/modifier/where/conjunction.h> | |||
#include <cpphibernate/modifier/where/disjunction.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* where_builder */ | |||
@@ -27,7 +28,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto build_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_and<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_and_v<mp::decay_t<T_clause>>> | |||
{ | |||
os << "("; | |||
build_clause(p_clause.clauses[hana::size_c<0>]); | |||
@@ -43,7 +44,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto build_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_or<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_or_v<mp::decay_t<T_clause>>> | |||
{ | |||
os << "("; | |||
build_clause(p_clause.clauses[hana::size_c<0>]); | |||
@@ -59,7 +60,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto build_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_not_v<mp::decay_t<T_clause>>> | |||
{ | |||
os << "NOT ("; | |||
build_clause(p_clause.clause); | |||
@@ -68,14 +69,14 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto build_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_equal_v<mp::decay_t<T_clause>>> | |||
{ | |||
auto field_id = misc::get_type_id(hana::type_c<mp::decay_t<decltype(p_clause.field)>>); | |||
auto field_id = get_type_id(hana::type_c<mp::decay_t<decltype(p_clause.field)>>); | |||
auto& field = schema.field(field_id); | |||
os << "`" | |||
<< field.table_name | |||
<< field.table.name | |||
<< "`.`" | |||
<< field.field_name | |||
<< field.name | |||
<< "`=" | |||
<< field.convert_to_open | |||
<< "?\?" | |||
@@ -87,7 +88,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_type>; | |||
using is_where_type = is_where_modifier<modifier_type>; | |||
hana::eval_if( | |||
is_where_type { }, | |||
[&](auto _){ | |||
@@ -115,9 +116,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto assign_clause(T_clause&& p_clause) | |||
-> mp::enable_if_c< | |||
modifier::is_where_clause_and<mp::decay_t<T_clause>>::value | |||
|| modifier::is_where_clause_or <mp::decay_t<T_clause>>::value> | |||
-> mp::enable_if_t< | |||
is_and_v<mp::decay_t<T_clause>> | |||
|| is_or_v <mp::decay_t<T_clause>>> | |||
{ | |||
hana::for_each(std::forward<T_clause>(p_clause).clauses, [&](auto& x_clause) { | |||
assign_clause(x_clause); | |||
@@ -126,14 +127,14 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_clause> | |||
inline auto assign_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_not_v<mp::decay_t<T_clause>>> | |||
{ | |||
assign_clause(std::forward<T_clause>(p_clause).clause); | |||
} | |||
template<typename T_clause> | |||
inline auto assign_clause(T_clause&& p_clause) | |||
-> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
-> mp::enable_if_t<is_equal_v<mp::decay_t<T_clause>>> | |||
{ | |||
statement.set(index, std::forward<T_clause>(p_clause).value); | |||
++index; | |||
@@ -143,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_type>; | |||
using is_where_type = is_where_modifier<modifier_type>; | |||
hana::eval_if( | |||
is_where_type { }, | |||
[&](auto _){ | |||
@@ -178,5 +179,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
return builder.assign(schema, modifiers); | |||
} | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -5,6 +5,16 @@ | |||
namespace cpphibernate | |||
{ | |||
namespace __impl | |||
{ | |||
/** | |||
* @brief Tag class all modifiers are derived from. | |||
*/ | |||
struct tag_modifier; | |||
} | |||
/** | |||
* @brief Evaluates to true_t if the passed type is a modifier, false_t otherwise. | |||
*/ | |||
@@ -2,8 +2,8 @@ | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace schema { | |||
namespace cpphibernate | |||
{ | |||
namespace __impl | |||
{ | |||
@@ -33,6 +33,6 @@ namespace schema { | |||
*/ | |||
constexpr decltype(auto) make_modifiers = mp::generic_predicate<__impl::modifiers_builder> { }; | |||
} } | |||
} | |||
#include "modifiers.inl" |
@@ -3,8 +3,8 @@ | |||
#include "modifier.h" | |||
#include "modifiers.h" | |||
namespace cpphibernate { | |||
namespace schema { | |||
namespace cpphibernate | |||
{ | |||
namespace __impl | |||
{ | |||
@@ -60,4 +60,4 @@ namespace schema { | |||
: public __impl::is_modifiers_impl<T> | |||
{ }; | |||
} } | |||
} |
@@ -14,6 +14,7 @@ namespace cpphibernate | |||
template<typename... T_order_directions> | |||
struct order_by_t | |||
: public tag_modifier | |||
, public tag_equality_comparable<tag_modifier> | |||
{ | |||
using order_directions_type = hana::basic_tuple<T_order_directions...>; | |||
@@ -1,6 +1,7 @@ | |||
#pragma once | |||
#include "where.h" | |||
#include "modifier.h" | |||
#include "where/where_clause.h" | |||
namespace cpphibernate | |||
@@ -14,6 +15,7 @@ namespace cpphibernate | |||
template<typename T_where_clause> | |||
struct where_t | |||
: public tag_modifier | |||
, public tag_equality_comparable<tag_modifier> | |||
{ | |||
using clause_type = T_where_clause; | |||
@@ -80,11 +80,17 @@ namespace schema { | |||
struct get_base_type_builder; | |||
/** | |||
* @brief Helper type to get the derived types of the passed dataset type. | |||
* @brief Helper class to get a list of the directly derived types of the passed dataset type. | |||
*/ | |||
template<typename X, typename = void> | |||
struct get_derived_types_builder; | |||
/** | |||
* @brief Helper class to get a list of all derived types (including the passed dataset type). | |||
*/ | |||
template<typename X, typename = void> | |||
struct get_all_derived_types_builder; | |||
} | |||
/** | |||
@@ -110,10 +116,15 @@ namespace schema { | |||
constexpr decltype(auto) get_base_type = mp::generic_predicate<__impl::get_base_type_builder> { }; | |||
/** | |||
* @brief Predicate to get the derived types of the passed dataset type. | |||
* @brief Predicate to get a list of the directly derived types of the passed dataset type. | |||
*/ | |||
constexpr decltype(auto) get_derived_types = mp::generic_predicate<__impl::get_derived_types_builder> { }; | |||
/** | |||
* @brief Predicate to get a list of all derived types (including the passed dataset type). | |||
*/ | |||
constexpr decltype(auto) get_all_derived_types = mp::generic_predicate<__impl::get_all_derived_types_builder> { }; | |||
} } | |||
#include "schema.inl" |
@@ -184,6 +184,48 @@ namespace schema { | |||
} | |||
}; | |||
/* get_all_derived_types_builder */ | |||
template<typename X, typename> | |||
struct get_all_derived_types_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&...) | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for get_all_derived_types(...)!"); } | |||
}; | |||
template<typename T_schema, typename T_dataset> | |||
struct get_all_derived_types_builder< | |||
mp::list<T_schema, T_dataset>, | |||
mp::enable_if_t< | |||
schema::is_schema_v<mp::decay_t<T_schema>> | |||
&& mp::is_same_v<hana::type_tag, hana::tag_of_t<T_dataset>> | |||
>> | |||
{ | |||
struct is_derived_impl | |||
{ | |||
using dataset_type = decay_unwrap_t<T_dataset>; | |||
template<typename T_type> | |||
constexpr decltype(auto) operator()(T_type&&) const | |||
{ | |||
return hana::bool_c< | |||
std::is_base_of_v<dataset_type, decay_unwrap_t<T_type>>>; | |||
} | |||
}; | |||
static constexpr decltype(auto) is_derived = is_derived_impl { }; | |||
static constexpr decltype(auto) apply(T_schema&& schema, T_dataset&& dataset) | |||
{ | |||
constexpr decltype(auto) dataset_types = | |||
decltype(hana::transform(schema.tables, schema::get_wrapped_dataset)) { }; | |||
constexpr decltype(auto) derived_types = | |||
decltype(hana::filter(dataset_types, is_derived)) { }; | |||
return derived_types; | |||
} | |||
}; | |||
} | |||
/* is_schema */ | |||
@@ -95,7 +95,13 @@ namespace schema { | |||
* @brief Helper type to get the wrapped dataset of the passed table. | |||
*/ | |||
template<typename X, typename = void> | |||
struct table_get_wrapped_dataset; | |||
struct get_wrapped_dataset_builder; | |||
/** | |||
* @brief Helper type to get the primary key field of the passed table. | |||
*/ | |||
template<typename X, typename = void> | |||
struct get_primary_key_field_builder; | |||
} | |||
@@ -119,7 +125,12 @@ namespace schema { | |||
/** | |||
* @brief Predicate to get the wrapped dataset of the passed table. | |||
*/ | |||
constexpr decltype(auto) get_wrapped_dataset = mp::generic_predicate<__impl::table_get_wrapped_dataset> { }; | |||
constexpr decltype(auto) get_wrapped_dataset = mp::generic_predicate<__impl::get_wrapped_dataset_builder> { }; | |||
/** | |||
* @brief Predicate to get the primary key field of the passed table. | |||
*/ | |||
constexpr decltype(auto) get_primary_key_field = mp::generic_predicate<__impl::get_primary_key_field_builder> { }; | |||
} } | |||
@@ -91,10 +91,10 @@ namespace schema { | |||
} | |||
}; | |||
/* table_get_wrapped_dataset */ | |||
/* get_wrapped_dataset */ | |||
template<typename X, typename> | |||
struct table_get_wrapped_dataset | |||
struct get_wrapped_dataset_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&...) | |||
@@ -102,7 +102,7 @@ namespace schema { | |||
}; | |||
template<typename T_table> | |||
struct table_get_wrapped_dataset< | |||
struct get_wrapped_dataset_builder< | |||
mp::list<T_table>, | |||
mp::enable_if_t<is_table_v<mp::decay_t<T_table>>>> | |||
{ | |||
@@ -110,6 +110,58 @@ namespace schema { | |||
{ return std::forward<T_table>(table).wrapped_dataset; } | |||
}; | |||
/* get_primary_key_field_builder */ | |||
template<typename X, typename> | |||
struct get_primary_key_field_builder | |||
{ | |||
template <typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for get_primary_key_field(...)!"); } | |||
}; | |||
template<typename T_table> | |||
struct get_primary_key_field_builder< | |||
mp::list<T_table>, | |||
mp::enable_if_t< | |||
is_table_v<mp::decay_t<T_table>>>> | |||
{ | |||
template<size_t I> | |||
using is_primary_key_field = decltype(hana::contains( | |||
std::declval<T_table>().fields[hana::size_c<I>].attributes, | |||
attribute::primary_key)); | |||
template<size_t I, size_t N, typename = void> | |||
struct helper; | |||
template<size_t N> | |||
struct helper<N, N, void> | |||
{ static_assert(N != N, "Unable to find primary key field for table!"); }; | |||
template<size_t I, size_t N> | |||
struct helper<I, N, mp::enable_if_t< | |||
!is_primary_key_field<I>::value>> | |||
{ | |||
static constexpr decltype(auto) apply(T_table&& table) | |||
{ return helper<I+1, N>::apply(std::forward<T_table>(table)); } | |||
}; | |||
template<size_t I, size_t N> | |||
struct helper<I, N, mp::enable_if_t< | |||
is_primary_key_field<I>::value>> | |||
{ | |||
static constexpr decltype(auto) apply(T_table&& table) | |||
{ return std::forward<T_table>(table).fields[hana::size_c<I>]; } | |||
}; | |||
static constexpr decltype(auto) apply(T_table&& table) | |||
{ | |||
using count = mp::decay_t<decltype(hana::size(std::declval<T_table>().fields))>; | |||
return helper<0, count::value>::apply( | |||
std::forward<T_table>(table)); | |||
} | |||
}; | |||
} | |||
/* is_table */ | |||
@@ -15,6 +15,12 @@ namespace schema { | |||
template<typename T, typename = void> | |||
struct tables_builder; | |||
/** | |||
* @brief Helper class to find a table by it's datatype. | |||
*/ | |||
template<typename X, typename = void> | |||
struct find_table_builder; | |||
} | |||
/** | |||
@@ -34,6 +40,11 @@ namespace schema { | |||
*/ | |||
constexpr decltype(auto) make_tables = mp::generic_predicate<__impl::tables_builder> { }; | |||
/** | |||
* @brief Predicate to find a table by it's dataset type. | |||
*/ | |||
constexpr decltype(auto) find_table = mp::generic_predicate<__impl::find_table_builder> { }; | |||
} } | |||
#include "tables.inl" |
@@ -37,6 +37,54 @@ namespace schema { | |||
} | |||
}; | |||
/* find_table_builder */ | |||
template<typename X, typename> | |||
struct find_table_builder | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for find_table(...)!"); } | |||
}; | |||
template<typename T_tables, typename T_dataset> | |||
struct find_table_builder< | |||
mp::list<T_tables, T_dataset>, | |||
mp::enable_if_t< | |||
is_tables_v<mp::decay_t<T_tables>> | |||
&& mp::is_same_v<hana::type_tag, hana::tag_of_t<T_dataset>> | |||
>> | |||
{ | |||
template<size_t I, size_t N, typename = void> | |||
struct helper; | |||
template<size_t N> | |||
struct helper<N, N, void> | |||
{ static_assert(N != N, "Table for given datatype does not exist!"); }; | |||
template<size_t I, size_t N> | |||
struct helper<I, N, mp::enable_if_t< | |||
decltype(hana::not_equal(std::declval<T_tables>()[hana::size_c<I>].wrapped_dataset, T_dataset { }))::value>> | |||
{ | |||
static constexpr decltype(auto) apply(T_tables&& tables) | |||
{ return helper<I+1, N>::apply(std::forward<T_tables>(tables)); } | |||
}; | |||
template<size_t I, size_t N> | |||
struct helper<I, N, mp::enable_if_t< | |||
decltype(hana::equal(std::declval<T_tables>()[hana::size_c<I>].wrapped_dataset, T_dataset { }))::value>> | |||
{ | |||
static constexpr decltype(auto) apply(T_tables&& tables) | |||
{ return std::forward<T_tables>(tables)[hana::size_c<I>]; } | |||
}; | |||
static constexpr decltype(auto) apply(T_tables&& tables, T_dataset&&) | |||
{ | |||
using count_type = mp::decay_t<decltype(hana::size(std::declval<T_tables>()))>; | |||
return helper<0, count_type::value>::apply(std::forward<T_tables>(tables)); | |||
} | |||
}; | |||
/* is_tables_impl */ | |||
template<typename T, typename = void> | |||
@@ -0,0 +1,66 @@ | |||
#include <set> | |||
#include <cpphibernate/driver/mariadb/classes/fields/field.h> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
#include <cpphibernate/driver/mariadb/context/create_update_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
static std::string build_foreign_many_update_query(const field_t& field); | |||
void field_t::foreign_many_update( | |||
const create_update_context& context, | |||
const std::string& primary_key) const | |||
{ | |||
auto& statement = get_statement_foreign_many_update(); | |||
auto& connection = context.connection; | |||
statement.set(0, primary_key); | |||
cpphibernate_log_debug("execute UPDATE old foreign many query: " << std::endl << update_statement.query(connection) << std::endl); | |||
connection.execute(statement); | |||
} | |||
::cppmariadb::statement& field_t::get_statement_foreign_many_update() const | |||
{ | |||
if (!_statement_foreign_many_update) | |||
{ | |||
auto query = build_foreign_many_update_query(*this); | |||
_statement_foreign_many_update.reset(new ::cppmariadb::statement(std::move(query))); | |||
} | |||
return *_statement_foreign_many_update; | |||
} | |||
std::string build_foreign_many_update_query(const field_t& field) | |||
{ | |||
assert(field.referenced_table); | |||
assert(field.referenced_table->primary_key_field); | |||
auto& ref_key_info = *field.referenced_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "UPDATE `" | |||
<< ref_key_info.table.name | |||
<< "` SET `" | |||
<< field.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`=NULL"; | |||
if (field.value_is_container) | |||
{ | |||
os << ", `" | |||
<< field.table.name | |||
<< "_index_" | |||
<< field.name | |||
<< "`=0"; | |||
} | |||
os << " WHERE `" | |||
<< field.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`=" | |||
<< ref_key_info.convert_to_open | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close; | |||
return os.str(); | |||
} |
@@ -0,0 +1,125 @@ | |||
#include <cpphibernate/driver/mariadb/classes/fields/field.h> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
#include <cpphibernate/driver/mariadb/context/create_update_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
static std::string build_foreign_one_delete_query(const field_t& field, bool key_known); | |||
void field_t::foreign_one_delete( | |||
const create_update_context& context, | |||
const std::string& primary_key, | |||
const value_t& foreign_key) const | |||
{ | |||
/* get statement */ | |||
auto& statement = foreign_key.has_value() | |||
? get_statement_foreign_one_delete_key_known() | |||
: get_statement_foreign_one_delete_key_unknown(); | |||
/* update statement values */ | |||
statement.set(0, primary_key); | |||
if (foreign_key.has_value()) | |||
statement.set(1, *foreign_key); | |||
/* execute query */ | |||
auto& connection = context.connection; | |||
cpphibernate_log_debug("execute DELETE old foreign one query: " << std::endl << statement.query(connection) << std::endl); | |||
connection.execute(statement); | |||
/* cleanup tables */ | |||
assert(referenced_table); | |||
table_t::table_set processed; | |||
referenced_table->cleanup(context, processed, true, true); | |||
} | |||
::cppmariadb::statement& field_t::get_statement_foreign_one_delete_key_known() const | |||
{ | |||
if (!_statement_foreign_one_delete_key_known) | |||
{ | |||
auto query = build_foreign_one_delete_query(*this, true); | |||
_statement_foreign_one_delete_key_known.reset(new ::cppmariadb::statement(std::move(query))); | |||
} | |||
return *_statement_foreign_one_delete_key_known; | |||
} | |||
cppmariadb::statement& field_t::get_statement_foreign_one_delete_key_unknown() const | |||
{ | |||
if (!_statement_foreign_one_delete_key_unknown) | |||
{ | |||
auto query = build_foreign_one_delete_query(*this, false); | |||
_statement_foreign_one_delete_key_unknown.reset(new ::cppmariadb::statement(std::move(query))); | |||
} | |||
return *_statement_foreign_one_delete_key_unknown; | |||
} | |||
std::string build_foreign_one_delete_query(const field_t& field, bool key_known) | |||
{ | |||
assert(field.table.primary_key_field); | |||
assert(field.referenced_table); | |||
assert(field.referenced_table->primary_key_field); | |||
if (key_known) | |||
{ | |||
auto& ref_table = *field.referenced_table; | |||
auto& key_info = *field.table.primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< ref_key_info.name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "` FROM `" | |||
<< key_info.table.name | |||
<< "` WHERE `" | |||
<< key_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< " AND `" | |||
<< ref_key_info.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`!=" | |||
<< ref_key_info.convert_to_open | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close | |||
<< ")"; | |||
auto where = os.str(); | |||
auto query = ref_table.build_delete_query(&where); | |||
return query; | |||
} | |||
else | |||
{ | |||
auto& ref_table = *field.referenced_table; | |||
auto& key_info = *field.table.primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< ref_key_info.name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "` FROM `" | |||
<< key_info.table.name | |||
<< "` WHERE `" | |||
<< key_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< ")"; | |||
auto where = os.str(); | |||
auto query = ref_table.build_delete_query(&where); | |||
return query; | |||
} | |||
} |
@@ -140,3 +140,4 @@ throw_not_implemented(void, set, const data_context& context, const value_t& val | |||
throw_not_implemented(bool, is_default, const data_context& context) | |||
throw_not_implemented(std::string, generate_value, ::cppmariadb::connection& connection) | |||
throw_not_implemented(value_t, foreign_create_update, const create_update_context& context) | |||
throw_not_implemented(read_context_ptr_u, foreign_read, const read_context& context, bool create_fake) |
@@ -275,7 +275,6 @@ std::string execute_create_update( | |||
auto new_context = context; | |||
if (!new_context.derived_table) | |||
new_context.derived_table = &table; | |||
// TODO std::string key = create_update_base(new_context); | |||
std::string key = table.base_table->create_update_exec(new_context); | |||
if (statement) statement->set(index, std::move(key)); | |||
++index; | |||
@@ -315,17 +314,7 @@ std::string execute_create_update( | |||
/* cleanup old dataset (if new one was created) */ | |||
if (context.is_update()) | |||
{ | |||
/* TODO | |||
auto& delete_statement = field.get_statement_foreign_one_delete(key.has_value()); | |||
delete_statement.set(0, primary_key); | |||
if (key.has_value()) | |||
delete_statement.set(1, *key); | |||
cpphibernate_log_debug("execute DELETE old foreign one query: " << std::endl << delete_statement.query(connection) << std::endl); | |||
connection.execute(delete_statement); | |||
table_set processed; | |||
field.referenced_table->destroy_cleanup(context, processed, true, true); | |||
*/ | |||
field.foreign_one_delete(context, primary_key, key); | |||
} | |||
} | |||
@@ -454,12 +443,7 @@ std::string execute_create_update( | |||
/* set foreign keys of existing elements to null */ | |||
if (context.is_update()) | |||
{ | |||
/* TODO | |||
auto& update_statement = field.get_statement_foreign_many_update(); | |||
update_statement.set(0, primary_key); | |||
cpphibernate_debug_log("execute UPDATE old foreign many query: " << update_statement.query(connection)); | |||
connection.execute(update_statement); | |||
*/ | |||
field.foreign_many_update(context, primary_key); | |||
} | |||
/* update elements */ | |||
@@ -472,10 +456,8 @@ std::string execute_create_update( | |||
/* delete non referenced elements */ | |||
if (context.is_update()) | |||
{ | |||
/* TODO | |||
table_set processed; | |||
ref_table.destroy_cleanup(context, processed, true, true); | |||
*/ | |||
table_t::table_set processed; | |||
ref_table.cleanup(context, processed, true, true); | |||
} | |||
} | |||
@@ -0,0 +1,303 @@ | |||
#include <list> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
#include <cpphibernate/driver/mariadb/context/base_context.inl> | |||
#include <cpphibernate/driver/mariadb/context/destroy_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
struct delete_query_builder_t | |||
{ | |||
const table_t& this_table; | |||
size_t alias_id { 0 }; | |||
std::ostringstream os; | |||
std::list<std::string> names; | |||
delete_query_builder_t( | |||
const table_t& p_this_table) | |||
: this_table(p_this_table) | |||
{ } | |||
inline std::string make_alias() | |||
{ return std::string("T") + std::to_string(alias_id++); } | |||
inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) | |||
{ | |||
auto has_alias = !alias.empty(); | |||
auto real_alias = has_alias ? alias : table.name; | |||
if (table.base_table && add_base) | |||
{ | |||
assert(table.base_table->primary_key_field); | |||
auto& base_table = *table.base_table; | |||
auto& base_key = *base_table.primary_key_field; | |||
auto base_alias = has_alias ? make_alias() : std::string(); | |||
auto real_base_alias = has_alias ? base_alias : base_table.name; | |||
os << " LEFT JOIN `" | |||
<< base_table.name; | |||
if (has_alias) | |||
{ | |||
os << "` AS `" | |||
<< base_alias; | |||
} | |||
os << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< base_key.name | |||
<< "`=`" | |||
<< real_base_alias | |||
<< "`.`" | |||
<< base_key.name | |||
<< "`"; | |||
add_table(base_table, base_alias, true, false); | |||
} | |||
names.emplace_back(real_alias); | |||
/* foreign table one */ | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->table.primary_key_field); | |||
assert(ptr->referenced_table); | |||
assert(ptr->referenced_table->primary_key_field); | |||
auto& field = *ptr; | |||
auto& own_key = *table.primary_key_field; | |||
auto& ref_table = *field.referenced_table; | |||
auto& ref_key = *ref_table.primary_key_field; | |||
auto new_alias = make_alias(); | |||
if (ref_table.is_used_in_container) | |||
{ | |||
os << " LEFT JOIN `" | |||
<< ref_table.name | |||
<< "` AS `" | |||
<< new_alias | |||
<< "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< own_key.name | |||
<< "`=`" | |||
<< new_alias | |||
<< "`.`" | |||
<< table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`"; | |||
} | |||
else | |||
{ | |||
os << " LEFT JOIN `" | |||
<< ref_table.name | |||
<< "` AS `" | |||
<< new_alias | |||
<< "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< ref_key.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`=`" | |||
<< new_alias | |||
<< "`.`" | |||
<< ref_key.name | |||
<< "`"; | |||
} | |||
add_table(ref_table, new_alias, true, true); | |||
} | |||
/* derived tables */ | |||
if (add_derived) | |||
{ | |||
for (auto& ptr : table.derived_tables) | |||
{ | |||
assert(ptr); | |||
assert(ptr->primary_key_field); | |||
auto& derived_table = *ptr; | |||
auto& primary_key = *table.primary_key_field; | |||
auto derived_alias = has_alias ? make_alias() : std::string(); | |||
auto real_derived_alias = has_alias ? derived_alias : derived_table.name; | |||
os << " LEFT JOIN `" | |||
<< derived_table.name; | |||
if (has_alias) | |||
{ | |||
os << "` AS `" | |||
<< derived_alias; | |||
} | |||
os << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< primary_key.name | |||
<< "`=`" | |||
<< real_derived_alias | |||
<< "`.`" | |||
<< primary_key.name | |||
<< "`"; | |||
add_table(derived_table, derived_alias, false, true); | |||
} | |||
} | |||
} | |||
inline std::string operator()(const std::string* where) | |||
{ | |||
os << " FROM `" | |||
<< this_table.name | |||
<< "`"; | |||
add_table(this_table, "", true, true); | |||
if (where) | |||
{ | |||
os << " " << *where; | |||
} | |||
else | |||
{ | |||
os << " ?where!"; | |||
} | |||
std::ostringstream ss; | |||
ss << "DELETE "; | |||
bool first = true; | |||
for (auto& name : names) | |||
{ | |||
if (first) | |||
first = false; | |||
else | |||
ss << ", "; | |||
ss << "`" | |||
<< name | |||
<< "`"; | |||
} | |||
ss << os.str(); | |||
return ss.str(); | |||
} | |||
}; | |||
static std::string build_foreign_many_delete_query(const table_t& table); | |||
void table_t::destroy(const destroy_context& context) const | |||
{ return destroy_exec(context); } | |||
void table_t::cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const | |||
{ | |||
/* check and add the table to the set of processed tables */ | |||
if (processed.count(this)) | |||
return; | |||
processed.emplace(this); | |||
/* execute the cleanup query */ | |||
foreign_many_delete_exec(context); | |||
/* cleanup all foreign fields */ | |||
for (auto ptr : foreign_table_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& ref_table = *ptr->referenced_table; | |||
if (ref_table.is_used_in_container) | |||
ref_table.cleanup(context, processed, true, true); | |||
} | |||
/* cleanup the base table */ | |||
if (check_base && base_table) | |||
{ | |||
base_table->cleanup(context, processed, false, true); | |||
} | |||
/* cleanup the dereived tables */ | |||
if (check_derived) | |||
{ | |||
for (auto ptr : derived_tables) | |||
{ | |||
assert(ptr); | |||
ptr->cleanup(context, processed, true, false); | |||
} | |||
} | |||
} | |||
void table_t::destroy_exec(const destroy_context& context) const | |||
{ | |||
assert(primary_key_field); | |||
auto& connection = context.connection; | |||
auto& statement = get_statement_delete(); | |||
statement.set(0, context.where); | |||
cpphibernate_log_debug("execute DELETE query: " << std::endl << statement.query(connection) << std::endl); | |||
connection.execute(statement); | |||
table_set processed; | |||
for (auto& ptr : foreign_table_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& ref_table = *ptr->referenced_table; | |||
if (ref_table.is_used_in_container) | |||
ref_table.cleanup(context, processed, true, true); | |||
} | |||
} | |||
void table_t::foreign_many_delete_exec(const base_context& context) const | |||
{ | |||
if (foreign_key_fields.empty()) | |||
return; | |||
auto& connection = context.connection; | |||
auto& statement = get_statement_foreign_many_delete(); | |||
cpphibernate_log_debug("execute DELETE old foreign many query: " << std::endl << statement.query(connection) << std::endl); | |||
connection.execute(statement); | |||
} | |||
std::string table_t::build_delete_query(const std::string* where) const | |||
{ return delete_query_builder_t(*this)(where); } | |||
::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const | |||
{ | |||
if (!_statement_foreign_many_delete) | |||
{ | |||
auto query = build_foreign_many_delete_query(*this); | |||
_statement_foreign_many_delete.reset(new ::cppmariadb::statement(std::move(query))); | |||
} | |||
return *_statement_foreign_many_delete; | |||
} | |||
::cppmariadb::statement& table_t::get_statement_delete() const | |||
{ | |||
if (!_statement_delete) | |||
{ | |||
auto query = delete_query_builder_t(*this)(nullptr); | |||
_statement_delete.reset(new ::cppmariadb::statement(std::move(query))); | |||
} | |||
return *_statement_delete; | |||
} | |||
std::string build_foreign_many_delete_query(const table_t& table) | |||
{ | |||
std::ostringstream os; | |||
auto first = true; | |||
os << "WHERE"; | |||
for (auto ptr : table.foreign_key_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (first) | |||
first = false; | |||
else | |||
os << " AND"; | |||
os << " (`" | |||
<< field.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "` IS NULL)"; | |||
} | |||
std::string where = os.str(); | |||
return delete_query_builder_t(table)(&where); | |||
} |
@@ -0,0 +1,541 @@ | |||
#include <cpphibernate/driver/mariadb/impl/filter.inl> | |||
#include <cpphibernate/driver/mariadb/context/read_context.inl> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
struct foreign_many_tuple_t | |||
{ | |||
const field_t& field; | |||
read_context_ptr_u context; | |||
std::string owner; | |||
}; | |||
struct foreign_many_list_t : | |||
public std::list<foreign_many_tuple_t> | |||
{ }; | |||
struct data_extractor_t | |||
{ | |||
const table_t& _table; | |||
const read_context& _context; | |||
const ::cppmariadb::row& _row; | |||
foreign_many_list_t& _foreign_many_list; | |||
const filter_t& _filter; | |||
mutable size_t _index; | |||
data_extractor_t( | |||
const table_t& p_table, | |||
const read_context& p_context, | |||
const ::cppmariadb::row& p_row, | |||
foreign_many_list_t& p_foreign_many_list) | |||
: _table (p_table) | |||
, _context (p_context) | |||
, _row (p_row) | |||
, _foreign_many_list(p_foreign_many_list) | |||
, _filter (p_context.filter) | |||
, _index (0) | |||
{ } | |||
inline bool has_value() const | |||
{ return !_row.at(_index).is_null(); } | |||
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 next_field() const | |||
{ ++_index; } | |||
inline void read_field(const field_t& field, const read_context& context, bool skip = false) const | |||
{ | |||
auto value = get_value(); | |||
++_index; | |||
if (!skip) | |||
field.set(context, value); | |||
} | |||
inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const | |||
{ | |||
/* read the base table */ | |||
if (read_base && table.base_table) | |||
{ | |||
skip = read_table(*table.base_table, context, true, false, skip); | |||
} | |||
/* create a dynamic dataset depending on the derived table */ | |||
else if ( read_base | |||
&& context.is_dynamic | |||
&& !table.derived_tables.empty()) | |||
{ | |||
auto value = get_value(); | |||
next_field(); | |||
if (static_cast<bool>(value) && !skip) | |||
{ | |||
auto type = cppcore::from_string<uint>(*value); | |||
auto derived = _table.get_derived_by_table_id(type); | |||
if (!derived) | |||
throw exception(std::string("unable to find dereived table for id ") + std::to_string(type)); | |||
derived->emplace(context); | |||
} | |||
else | |||
{ | |||
skip = true; | |||
} | |||
} | |||
/* create a static dataset */ | |||
else if (has_value() && !skip) | |||
{ | |||
if (read_base) | |||
{ | |||
context.emplace(); | |||
} | |||
} | |||
/* no data -> skip */ | |||
else | |||
{ | |||
skip = true; | |||
} | |||
if (_context.filter.is_excluded(table)) | |||
return skip; | |||
/* primary key */ | |||
assert(table.primary_key_field); | |||
read_field(*table.primary_key_field, context, skip); | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (!_context.filter.is_excluded(field)) | |||
read_field(field, context, skip); | |||
} | |||
/* foreign table one */ | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
auto& ref_table = *field.referenced_table; | |||
if ( _filter.is_excluded(field) | |||
|| _filter.is_excluded(ref_table)) | |||
continue; | |||
auto next_context = field.foreign_read(context, skip); | |||
assert(static_cast<bool>(next_context)); | |||
read_table(ref_table, *next_context, true, true, skip); | |||
next_context->finish(); | |||
} | |||
/* foreign table many */ | |||
if (!skip) | |||
{ | |||
for (auto& ptr : table.foreign_table_many_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
auto& ref_table = *field.referenced_table; | |||
if ( _filter.is_excluded(field) | |||
|| _filter.is_excluded(ref_table)) | |||
continue; | |||
_foreign_many_list.emplace_back( | |||
foreign_many_tuple_t { | |||
field, | |||
field.foreign_read(context, false), | |||
*table.primary_key_field->get(context), | |||
}); | |||
} | |||
} | |||
/* derived tables */ | |||
if (read_derived && context.is_dynamic) | |||
{ | |||
for (auto& ptr : table.derived_tables) | |||
{ | |||
assert(ptr); | |||
auto& derived_table = *ptr; | |||
read_table(derived_table, context, false, true, skip); | |||
} | |||
} | |||
return skip; | |||
} | |||
inline void operator()() const | |||
{ | |||
_index = 0; | |||
read_table(_table, _context, true, true, false); | |||
if (_index != _row.size()) | |||
throw exception("result was not completely read!"); | |||
} | |||
}; | |||
/* select_query_builder_t */ | |||
struct select_query_builder_t | |||
{ | |||
struct local_context | |||
{ | |||
const table_t& table; | |||
std::string alias; | |||
bool add_base; | |||
bool add_derived; | |||
bool is_dynamic; | |||
}; | |||
const table_t& _table; | |||
const filter_t& _filter; | |||
bool _is_dynamic; | |||
size_t alias_id { 0 }; | |||
size_t index { 0 }; | |||
std::ostringstream os; | |||
std::list<std::string> joins; | |||
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 std::string make_alias() | |||
{ return std::string("T") + std::to_string(alias_id++); } | |||
inline void add_field(const field_t& field, const std::string& alias) | |||
{ | |||
if (index++) os << ", "; | |||
os << field.convert_from_open | |||
<< "`" | |||
<< alias | |||
<< "`.`" | |||
<< field.name | |||
<< "`" | |||
<< field.convert_from_close; | |||
} | |||
inline bool add_table(const local_context& ctx) | |||
{ | |||
bool ret = false; | |||
auto has_alias = !ctx.alias.empty(); | |||
auto real_alias = has_alias | |||
? ctx.alias | |||
: ctx.table.name; | |||
if (ctx.table.base_table && ctx.add_base) | |||
{ | |||
assert(ctx.table.base_table->primary_key_field); | |||
auto& base_table = *ctx.table.base_table; | |||
auto& base_key = *base_table.primary_key_field; | |||
auto base_alias = has_alias ? make_alias() : std::string(); | |||
auto real_base_alias = has_alias ? base_alias : base_table.name; | |||
std::ostringstream ss; | |||
ss << " JOIN `" | |||
<< base_table.name; | |||
if (has_alias) | |||
{ | |||
ss << "` AS `" | |||
<< base_alias; | |||
} | |||
ss << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< base_key.name | |||
<< "`=`" | |||
<< real_base_alias | |||
<< "`.`" | |||
<< base_key.name | |||
<< "`"; | |||
auto it = joins.insert(joins.end(), ss.str()); | |||
if (add_table({ | |||
base_table, | |||
base_alias, | |||
true, | |||
false, | |||
ctx.is_dynamic | |||
})) | |||
{ | |||
ret = true; | |||
} | |||
else | |||
{ | |||
joins.erase(it); | |||
} | |||
} | |||
/* __type */ | |||
if ( ctx.is_dynamic | |||
&& !ctx.table.base_table | |||
&& !ctx.table.derived_tables.empty()) | |||
{ | |||
if (index++) os << ", "; | |||
os << "`" | |||
<< ctx.table.name | |||
<< "`.`__type` AS `__type`"; | |||
ret = true; | |||
} | |||
if (_filter.is_excluded(ctx.table)) | |||
return ret; | |||
/* primary key */ | |||
assert(ctx.table.primary_key_field); | |||
add_field(*ctx.table.primary_key_field, real_alias); | |||
ret = true; | |||
/* data fields */ | |||
for (auto& ptr : ctx.table.data_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (!_filter.is_excluded(field)) | |||
{ | |||
add_field(field, real_alias); | |||
} | |||
} | |||
/* foreign table one */ | |||
for (auto& ptr : ctx.table.foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->table.primary_key_field); | |||
assert(ptr->referenced_table); | |||
assert(ptr->referenced_table->primary_key_field); | |||
auto& field = *ptr; | |||
auto& table = field.table; | |||
auto& own_key = *table.primary_key_field; | |||
auto& ref_table = *field.referenced_table; | |||
auto& ref_key = *ref_table.primary_key_field; | |||
if ( _filter.is_excluded(field) | |||
|| _filter.is_excluded(ref_table)) | |||
continue; | |||
auto new_alias = make_alias(); | |||
std::ostringstream ss; | |||
if (ref_table.is_used_in_container) | |||
{ | |||
ss << " LEFT JOIN `" | |||
<< ref_table.name | |||
<< "` AS `" | |||
<< new_alias | |||
<< "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< own_key.name | |||
<< "`=`" | |||
<< new_alias | |||
<< "`.`" | |||
<< table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`"; | |||
} | |||
else | |||
{ | |||
ss << " LEFT JOIN `" | |||
<< ref_table.name | |||
<< "` AS `" | |||
<< new_alias | |||
<< "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< ref_key.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`=`" | |||
<< new_alias | |||
<< "`.`" | |||
<< ref_key.name | |||
<< "`"; | |||
} | |||
auto it = joins.insert(joins.end(), ss.str()); | |||
if (!add_table({ | |||
ref_table, | |||
new_alias, | |||
true, | |||
true, | |||
field.value_is_pointer | |||
})) | |||
{ | |||
joins.erase(it); | |||
} | |||
} | |||
/* derived tables */ | |||
if (ctx.add_derived && ctx.is_dynamic) | |||
{ | |||
for (auto& ptr : ctx.table.derived_tables) | |||
{ | |||
assert(ptr); | |||
assert(ptr->primary_key_field); | |||
auto& derived_table = *ptr; | |||
auto& primary_key = *ctx.table.primary_key_field; | |||
auto derived_alias = has_alias ? make_alias() : std::string(); | |||
auto real_derived_alias = has_alias ? derived_alias : derived_table.name; | |||
std::ostringstream ss; | |||
ss << " LEFT JOIN `" | |||
<< derived_table.name; | |||
if (has_alias) | |||
{ | |||
ss << "` AS `" | |||
<< derived_alias; | |||
} | |||
ss << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< primary_key.name | |||
<< "`=`" | |||
<< real_derived_alias | |||
<< "`.`" | |||
<< primary_key.name | |||
<< "`"; | |||
auto it = joins.insert(joins.end(), ss.str()); | |||
if (!add_table({ | |||
derived_table, | |||
derived_alias, | |||
false, | |||
true, | |||
ctx.is_dynamic, | |||
})) | |||
{ | |||
joins.erase(it); | |||
} | |||
} | |||
} | |||
return ret; | |||
} | |||
inline std::string operator()() | |||
{ | |||
os << "SELECT "; | |||
add_table({ | |||
_table, | |||
"", | |||
true, | |||
true, | |||
_is_dynamic, | |||
}); | |||
os << " FROM `" | |||
<< _table.name | |||
<< "`"; | |||
for (auto& join : joins) | |||
os << join; | |||
os << " ?where! ?order! ?limit!"; | |||
return os.str(); | |||
} | |||
}; | |||
void table_t::read(const read_context& context) const | |||
{ | |||
auto& statement = get_statement_select(context.filter, context.is_dynamic); | |||
auto& connection = context.connection; | |||
statement.set(0, context.where); | |||
statement.set(1, context.order_by); | |||
statement.set(2, context.limit); | |||
cpphibernate_log_debug("execute SELECT query: " << std::endl << statement.query(connection) << std::endl); | |||
auto result = connection.execute_used(statement); | |||
if (!result) | |||
throw exception("Unable to fetching data from database!"); | |||
::cppmariadb::row * row; | |||
foreign_many_list_t foreign_many_list; | |||
while ((row = result->next())) | |||
{ | |||
data_extractor_t(*this, context, *row, foreign_many_list)(); | |||
} | |||
context.finish(); | |||
for (auto& tuple : foreign_many_list) | |||
{ | |||
auto& field = tuple.field; | |||
auto& next_context = *tuple.context; | |||
assert(field.referenced_table); | |||
assert(field.referenced_table->primary_key_field); | |||
auto& ref_table = *field.referenced_table; | |||
auto& ref_field = *ref_table.primary_key_field; | |||
{ | |||
std::ostringstream ss; | |||
ss << "WHERE (`" | |||
<< ref_table.name | |||
<< "`.`" | |||
<< field.table.name | |||
<< "_id_" | |||
<< field.name | |||
<< "`=" | |||
<< ref_field.convert_to_open | |||
<< "'" | |||
<< context.connection.escape(tuple.owner) | |||
<< "'" | |||
<< ref_field.convert_to_close | |||
<< ")"; | |||
next_context.where = ss.str(); | |||
} | |||
{ | |||
std::ostringstream ss; | |||
ss << "ORDER BY `" | |||
<< ref_table.name | |||
<< "`.`" | |||
<< field.table.name | |||
<< "_index_" | |||
<< field.name | |||
<< "` ASC"; | |||
next_context.order_by = ss.str(); | |||
} | |||
ref_table.read(next_context); | |||
} | |||
} | |||
void table_t::emplace(const read_context& context) const | |||
{ throw exception(std::string("'") + name + "' does not implement the emplace() method!"); } | |||
::cppmariadb::statement& table_t::get_statement_select(const filter_t& filter, bool dynamic) const | |||
{ | |||
auto& map = dynamic | |||
? _statement_select_dynamic | |||
: _statement_select_static; | |||
auto key = std::make_tuple(filter.cache_id, static_cast<const field_t*>(nullptr)); | |||
auto it = map.find(key); | |||
if (it == map.end()) | |||
{ | |||
auto query = select_query_builder_t(*this, filter, dynamic)(); | |||
it = map.emplace(key, ::cppmariadb::statement(query)).first; | |||
} | |||
return it->second; | |||
} |
@@ -48,7 +48,7 @@ TEST(CppHibernateTests, create_test1) | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(t1); | |||
} | |||
#if 0 | |||
TEST(CppHibernateTests, create_test2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
@@ -86,7 +86,7 @@ TEST(CppHibernateTests, create_test2) | |||
t2.i16_data = 4; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(t2); | |||
} | |||
@@ -131,7 +131,7 @@ TEST(CppHibernateTests, create_test3) | |||
t3.i64_data = 8; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(t3); | |||
} | |||
@@ -196,7 +196,7 @@ TEST(CppHibernateTests, create_derived1) | |||
d1.test1_data.u32_ptr_s = std::make_shared<uint32_t>(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(static_cast<base&>(d1)); | |||
} | |||
@@ -278,7 +278,7 @@ TEST(CppHibernateTests, create_derived2) | |||
d2.test2_ptr_u->i16_data = 23; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(static_cast<base&>(d2)); | |||
} | |||
@@ -446,7 +446,7 @@ TEST(CppHibernateTests, create_derived3) | |||
d3.test3_vector.back().i64_data = 223; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(static_cast<derived2&>(d3)); | |||
} | |||
@@ -515,7 +515,7 @@ TEST(CppHibernateTests, create_dummy_owner) | |||
d.dummies.emplace_back(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(d); | |||
} | |||
@@ -569,7 +569,7 @@ TEST(CppHibernateTests, create_double_usage) | |||
d.multiple_items.emplace_back(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.create(d); | |||
EXPECT_EQ(d.id, 1); | |||
@@ -577,4 +577,3 @@ TEST(CppHibernateTests, create_double_usage) | |||
EXPECT_EQ(d.multiple_items[0].id, 1002); | |||
EXPECT_EQ(d.multiple_items[1].id, 1003); | |||
} | |||
#endif |
@@ -35,7 +35,7 @@ TEST(CppHibernateTests, destroy_test1) | |||
t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(t1); | |||
} | |||
@@ -67,7 +67,7 @@ TEST(CppHibernateTests, destroy_test2) | |||
t2.id = uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(t2); | |||
} | |||
@@ -99,7 +99,7 @@ TEST(CppHibernateTests, destroy_test3) | |||
t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(t3); | |||
} | |||
@@ -137,7 +137,7 @@ TEST(CppHibernateTests, destroy_derived1) | |||
d1.id = uuid("3d12778a-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(static_cast<base&>(d1)); | |||
} | |||
@@ -184,7 +184,7 @@ TEST(CppHibernateTests, destroy_derived2) | |||
d2.id = uuid("3d127db6-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(static_cast<base&>(d2)); | |||
} | |||
@@ -238,7 +238,7 @@ TEST(CppHibernateTests, destroy_derived3) | |||
d3.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(static_cast<derived2&>(d3)); | |||
} | |||
@@ -280,7 +280,6 @@ TEST(CppHibernateTests, destroy_double_usage) | |||
d.id = 1; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.destroy(d); | |||
} | |||
@@ -6,7 +6,6 @@ | |||
using namespace ::testing; | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::modifier; | |||
using namespace ::boost::hana::literals; | |||
TEST(CppHibernateTests, read_test1) | |||
@@ -45,9 +44,8 @@ TEST(CppHibernateTests, read_test1) | |||
t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
using namespace modifier; | |||
context.read(t1); | |||
EXPECT_EQ (t1.id, uuid("3d12697a-abb9-11e8-98d0-529269fb1459")); | |||
@@ -92,7 +90,7 @@ TEST(CppHibernateTests, read_test2) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
constexpr decltype(auto) test2_key_field = test_schema.tables[1_c].fields[0_c]; | |||
@@ -138,7 +136,7 @@ TEST(CppHibernateTests, read_test3) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
test3 t3; | |||
t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||
@@ -193,7 +191,7 @@ TEST(CppHibernateTests, read_derived1_static) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
derived1 d1; | |||
d1.derived1_id = uuid("3d12758c-abb9-11e8-98d0-529269fb1459"); | |||
@@ -266,7 +264,7 @@ TEST(CppHibernateTests, read_derived2_static) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
derived2 d2; | |||
d2.derived2_id = uuid("3d127bcc-abb9-11e8-98d0-529269fb1459"); | |||
@@ -378,7 +376,7 @@ TEST(CppHibernateTests, read_derived3_static) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
derived3 d3; | |||
d3.derived3_id = uuid("3d12866c-abb9-11e8-98d0-529269fb1459"); | |||
@@ -541,7 +539,7 @@ TEST(CppHibernateTests, read_derived2_ptr_dynamic) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
constexpr decltype(auto) d2_key_field = test_schema.tables[5_c].fields[0_c]; | |||
@@ -738,7 +736,7 @@ TEST(CppHibernateTests, read_base_ptr_vector_dynamic) | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
using base_ptr_type = std::unique_ptr<base>; | |||
using base_vec_type = std::vector<base_ptr_type>; | |||
@@ -908,7 +906,7 @@ TEST(CppHibernateTests, read_dummy_owner) | |||
d.id = uuid("00000000-0000-0000-0000-000000000001"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.read(d); | |||
@@ -967,7 +965,7 @@ TEST(CppHibernateTests, read_double_usage) | |||
d.id = 1; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.read(d); | |||
@@ -980,4 +978,67 @@ TEST(CppHibernateTests, read_double_usage) | |||
EXPECT_EQ (d.multiple_items[0].data, 456); | |||
EXPECT_EQ (d.multiple_items[1].id, 1003); | |||
EXPECT_EQ (d.multiple_items[1].data, 789); | |||
} | |||
} | |||
TEST(CppHibernateTests, read_with_modifiers) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "SELECT " | |||
"BinToUuid(`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` " | |||
"WHERE (" | |||
"`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')) " | |||
"ORDER_BY " | |||
"`tbl_test1`.`str_data` ASC, " | |||
"`tbl_test1`.`str64_data` DESC " | |||
"LIMIT 1 " | |||
"OFFSET 4", | |||
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, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
.Times(AnyNumber()) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
test1 t1; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
constexpr decltype(auto) test1_key_field = test_schema.tables[0_c].fields[0_c]; | |||
constexpr decltype(auto) test1_tmp_field_0 = test_schema.tables[0_c].fields[1_c]; | |||
constexpr decltype(auto) test1_tmp_field_1 = test_schema.tables[0_c].fields[2_c]; | |||
context.read(t1, | |||
where(equal(test1_key_field, "3d1270dc-abb9-11e8-98d0-529269fb1459")), | |||
limit(1_c), | |||
offset(4_c), | |||
order_by( | |||
ascending(test1_tmp_field_0), | |||
descending(test1_tmp_field_1))); | |||
EXPECT_EQ (t1.id, uuid("3d12697a-abb9-11e8-98d0-529269fb1459")); | |||
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); | |||
} |
@@ -44,7 +44,7 @@ TEST(CppHibernateTests, update_test1) | |||
t1.u32_ptr_s = std::make_shared<uint32_t>(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(t1); | |||
} | |||
@@ -84,7 +84,7 @@ TEST(CppHibernateTests, update_test2) | |||
t2.i16_data = 4; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(t2); | |||
} | |||
@@ -124,7 +124,7 @@ TEST(CppHibernateTests, update_test3) | |||
t3.i64_data = 8; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(t3); | |||
} | |||
@@ -200,7 +200,7 @@ TEST(CppHibernateTests, update_derived1) | |||
d1.test1_data.u32_ptr_s = std::make_shared<uint32_t>(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(static_cast<base&>(d1)); | |||
} | |||
@@ -320,7 +320,7 @@ TEST(CppHibernateTests, update_derived2) | |||
d2.test2_ptr_u->i16_data = 23; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(static_cast<base&>(d2)); | |||
} | |||
@@ -538,7 +538,7 @@ TEST(CppHibernateTests, update_derived3) | |||
d3.test3_vector.back().i64_data = 223; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(static_cast<derived2&>(d3)); | |||
} | |||
@@ -632,7 +632,7 @@ TEST(CppHibernateTests, update_dynamic_base) | |||
d2.name = "dynamic derived 1"; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(b); | |||
} | |||
@@ -707,7 +707,7 @@ TEST(CppHibernateTests, update_dummy_owner) | |||
d.dummies.emplace_back(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(d); | |||
} | |||
@@ -796,7 +796,7 @@ TEST(CppHibernateTests, update_double_usage) | |||
d.multiple_items.back().data = 789; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(d); | |||
} | |||
@@ -885,6 +885,6 @@ TEST(CppHibernateTests, update_double_usage_wrapper) | |||
d.double_usage->multiple_items.back().data = 789; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
auto context = make_context<mariadb_driver>(test_schema, connection); | |||
context.update(d); | |||
} | |||
} |