Browse Source

* Refactored rad and destroy operations

refactoring
bergmann 4 years ago
parent
commit
f77b957397
58 changed files with 2734 additions and 1065 deletions
  1. +3
    -3
      README.md
  2. +86
    -61
      include/cpphibernate/context/context.inl
  3. +58
    -0
      include/cpphibernate/driver/mariadb/classes/fields/field.h
  4. +20
    -0
      include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h
  5. +46
    -0
      include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.inl
  6. +117
    -9
      include/cpphibernate/driver/mariadb/classes/tables/table.h
  7. +31
    -2
      include/cpphibernate/driver/mariadb/classes/tables/table.inl
  8. +49
    -5
      include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h
  9. +136
    -11
      include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.inl
  10. +1
    -3
      include/cpphibernate/driver/mariadb/classes/tables/table_simple.h
  11. +27
    -2
      include/cpphibernate/driver/mariadb/context/data_context.h
  12. +23
    -3
      include/cpphibernate/driver/mariadb/context/data_context.inl
  13. +23
    -0
      include/cpphibernate/driver/mariadb/context/destroy_context.h
  14. +20
    -0
      include/cpphibernate/driver/mariadb/context/destroy_context.inl
  15. +104
    -0
      include/cpphibernate/driver/mariadb/context/read_context.h
  16. +364
    -0
      include/cpphibernate/driver/mariadb/context/read_context.inl
  17. +22
    -102
      include/cpphibernate/driver/mariadb/driver.h
  18. +11
    -0
      include/cpphibernate/driver/mariadb/driver.inl
  19. +27
    -0
      include/cpphibernate/driver/mariadb/helper/container_helper.h
  20. +43
    -0
      include/cpphibernate/driver/mariadb/helper/container_helper.inl
  21. +1
    -3
      include/cpphibernate/driver/mariadb/helper/key_properties.inl
  22. +0
    -2
      include/cpphibernate/driver/mariadb/helper/nullable_helper.h
  23. +4
    -6
      include/cpphibernate/driver/mariadb/helper/nullable_helper.inl
  24. +2
    -0
      include/cpphibernate/driver/mariadb/impl.h
  25. +0
    -234
      include/cpphibernate/driver/mariadb/impl/_context.h
  26. +0
    -144
      include/cpphibernate/driver/mariadb/impl/_context.inl
  27. +0
    -265
      include/cpphibernate/driver/mariadb/impl/_read.h
  28. +7
    -2
      include/cpphibernate/driver/mariadb/impl/create_update.inl
  29. +14
    -0
      include/cpphibernate/driver/mariadb/impl/destroy.h
  30. +35
    -19
      include/cpphibernate/driver/mariadb/impl/destroy.inl
  31. +20
    -54
      include/cpphibernate/driver/mariadb/impl/driver_impl.h
  32. +50
    -2
      include/cpphibernate/driver/mariadb/impl/driver_impl.inl
  33. +9
    -11
      include/cpphibernate/driver/mariadb/impl/driver_impl/limit.inl
  34. +5
    -9
      include/cpphibernate/driver/mariadb/impl/driver_impl/modifier_tags.inl
  35. +13
    -15
      include/cpphibernate/driver/mariadb/impl/driver_impl/order_by.inl
  36. +22
    -22
      include/cpphibernate/driver/mariadb/impl/driver_impl/where.inl
  37. +10
    -0
      include/cpphibernate/modifier/modifier.h
  38. +3
    -3
      include/cpphibernate/modifier/modifiers.h
  39. +3
    -3
      include/cpphibernate/modifier/modifiers.inl
  40. +1
    -0
      include/cpphibernate/modifier/order_by.inl
  41. +2
    -0
      include/cpphibernate/modifier/where.inl
  42. +13
    -2
      include/cpphibernate/schema/schema.h
  43. +42
    -0
      include/cpphibernate/schema/schema.inl
  44. +13
    -2
      include/cpphibernate/schema/table.h
  45. +55
    -3
      include/cpphibernate/schema/table.inl
  46. +11
    -0
      include/cpphibernate/schema/tables.h
  47. +48
    -0
      include/cpphibernate/schema/tables.inl
  48. +66
    -0
      src/cpphibernate/driver/mariadb/classes/fields/create_update.cpp
  49. +125
    -0
      src/cpphibernate/driver/mariadb/classes/fields/destroy.cpp
  50. +1
    -0
      src/cpphibernate/driver/mariadb/classes/fields/misc.cpp
  51. +4
    -22
      src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp
  52. +303
    -0
      src/cpphibernate/driver/mariadb/classes/tables/destroy.cpp
  53. +541
    -0
      src/cpphibernate/driver/mariadb/classes/tables/read.cpp
  54. +8
    -9
      test/cpphibernate/cpphibernate_create.cpp
  55. +7
    -8
      test/cpphibernate/cpphibernate_destroy.cpp
  56. +0
    -0
      test/cpphibernate/cpphibernate_init.cpp
  57. +74
    -13
      test/cpphibernate/cpphibernate_read.cpp
  58. +11
    -11
      test/cpphibernate/cpphibernate_update.cpp

+ 3
- 3
README.md View File

@@ -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

+ 86
- 61
include/cpphibernate/context/context.inl View File

@@ -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>


+ 58
- 0
include/cpphibernate/driver/mariadb/classes/fields/field.h View File

@@ -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>;


+ 20
- 0
include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h View File

@@ -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;
};

} }

+ 46
- 0
include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.inl View File

@@ -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));
}
}

} }

+ 117
- 9
include/cpphibernate/driver/mariadb/classes/tables/table.h View File

@@ -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>;


+ 31
- 2
include/cpphibernate/driver/mariadb/classes/tables/table.inl View File

@@ -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 {

};


}

} }

+ 49
- 5
include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h View File

@@ -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;
};

} }

+ 136
- 11
include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.inl View File

@@ -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
- 3
include/cpphibernate/driver/mariadb/classes/tables/table_simple.h View File

@@ -1,8 +1,6 @@
#pragma once




#include "table.h"

namespace cpphibernate {
namespace mariadb {


+ 27
- 2
include/cpphibernate/driver/mariadb/context/data_context.h View File

@@ -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;
};

} }

+ 23
- 3
include/cpphibernate/driver/mariadb/context/data_context.inl View File

@@ -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;
}



+ 23
- 0
include/cpphibernate/driver/mariadb/context/destroy_context.h View File

@@ -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);
};

} }

+ 20
- 0
include/cpphibernate/driver/mariadb/context/destroy_context.inl View File

@@ -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)
{ }

} }

+ 104
- 0
include/cpphibernate/driver/mariadb/context/read_context.h View File

@@ -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> { };

} }

+ 364
- 0
include/cpphibernate/driver/mariadb/context/read_context.inl View File

@@ -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)...); }
};

}

} }

+ 22
- 102
include/cpphibernate/driver/mariadb/driver.h View File

@@ -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;
};

} }

+ 11
- 0
include/cpphibernate/driver/mariadb/driver.inl View File

@@ -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; }



+ 27
- 0
include/cpphibernate/driver/mariadb/helper/container_helper.h View File

@@ -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;
};

} }

+ 43
- 0
include/cpphibernate/driver/mariadb/helper/container_helper.inl View File

@@ -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
- 3
include/cpphibernate/driver/mariadb/helper/key_properties.inl View File

@@ -1,8 +1,6 @@
#pragma once




#include "key_properties.h"

namespace cpphibernate {
namespace mariadb {


+ 0
- 2
include/cpphibernate/driver/mariadb/helper/nullable_helper.h View File

@@ -1,7 +1,5 @@
#pragma once



namespace cpphibernate {
namespace mariadb {



+ 4
- 6
include/cpphibernate/driver/mariadb/helper/nullable_helper.inl View File

@@ -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;


+ 2
- 0
include/cpphibernate/driver/mariadb/impl.h View File

@@ -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"

+ 0
- 234
include/cpphibernate/driver/mariadb/impl/_context.h View File

@@ -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
} }

+ 0
- 144
include/cpphibernate/driver/mariadb/impl/_context.inl View File

@@ -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
} }

+ 0
- 265
include/cpphibernate/driver/mariadb/impl/_read.h View File

@@ -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

+ 7
- 2
include/cpphibernate/driver/mariadb/impl/create_update.inl View File

@@ -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


+ 14
- 0
include/cpphibernate/driver/mariadb/impl/destroy.h View File

@@ -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;

} }

include/cpphibernate/driver/mariadb/impl/_destroy.h → include/cpphibernate/driver/mariadb/impl/destroy.inl View File

@@ -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
} }

+ 20
- 54
include/cpphibernate/driver/mariadb/impl/driver_impl.h View File

@@ -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;
};

} }

+ 50
- 2
include/cpphibernate/driver/mariadb/impl/driver_impl.inl View File

@@ -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));
}

} }

include/cpphibernate/driver/mariadb/impl/_limit.h → include/cpphibernate/driver/mariadb/impl/driver_impl/limit.inl View File

@@ -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
} }

include/cpphibernate/driver/mariadb/impl/_modifier_tags.h → include/cpphibernate/driver/mariadb/impl/driver_impl/modifier_tags.inl View File

@@ -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
} }

include/cpphibernate/driver/mariadb/impl/_order_by.h → include/cpphibernate/driver/mariadb/impl/driver_impl/order_by.inl View File

@@ -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
} }

include/cpphibernate/driver/mariadb/impl/_where.h → include/cpphibernate/driver/mariadb/impl/driver_impl/where.inl View File

@@ -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
} }

+ 10
- 0
include/cpphibernate/modifier/modifier.h View File

@@ -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.
*/


+ 3
- 3
include/cpphibernate/modifier/modifiers.h View File

@@ -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
- 3
include/cpphibernate/modifier/modifiers.inl View File

@@ -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>
{ };

} }
}

+ 1
- 0
include/cpphibernate/modifier/order_by.inl View File

@@ -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...>;



+ 2
- 0
include/cpphibernate/modifier/where.inl View File

@@ -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;



+ 13
- 2
include/cpphibernate/schema/schema.h View File

@@ -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"

+ 42
- 0
include/cpphibernate/schema/schema.inl View File

@@ -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 */


+ 13
- 2
include/cpphibernate/schema/table.h View File

@@ -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> { };

} }



+ 55
- 3
include/cpphibernate/schema/table.inl View File

@@ -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 */


+ 11
- 0
include/cpphibernate/schema/tables.h View File

@@ -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"

+ 48
- 0
include/cpphibernate/schema/tables.inl View File

@@ -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>


+ 66
- 0
src/cpphibernate/driver/mariadb/classes/fields/create_update.cpp View File

@@ -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();
}

+ 125
- 0
src/cpphibernate/driver/mariadb/classes/fields/destroy.cpp View File

@@ -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;
}
}

+ 1
- 0
src/cpphibernate/driver/mariadb/classes/fields/misc.cpp View File

@@ -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)

+ 4
- 22
src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp View File

@@ -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);
}
}



+ 303
- 0
src/cpphibernate/driver/mariadb/classes/tables/destroy.cpp View File

@@ -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);
}

+ 541
- 0
src/cpphibernate/driver/mariadb/classes/tables/read.cpp View File

@@ -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;
}

+ 8
- 9
test/cpphibernate/cpphibernate_create.cpp View File

@@ -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

test/cpphibernate/cpphibernate_destroy.xcpp → test/cpphibernate/cpphibernate_destroy.cpp View File

@@ -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);
}


test/cpphibernate/cpphibernate_init.xcpp → test/cpphibernate/cpphibernate_init.cpp View File


test/cpphibernate/cpphibernate_read.xcpp → test/cpphibernate/cpphibernate_read.cpp View File

@@ -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);
}

test/cpphibernate/cpphibernate_update.xcpp → test/cpphibernate/cpphibernate_update.cpp View File

@@ -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);
}
}

Loading…
Cancel
Save