* Refactored includes (removed ring dependencies)refactoring
@@ -5,3 +5,5 @@ | |||
#include "cpphibernate/modifier.h" | |||
#include "cpphibernate/schema.h" | |||
#include "cpphibernate/types.h" | |||
#include "cpphibernate/types.inl" |
@@ -1,3 +1,5 @@ | |||
#pragma once | |||
#include "context/context.h" | |||
#include "context/context.inl" |
@@ -102,5 +102,3 @@ namespace cpphibernate | |||
constexpr decltype(auto) make_context_ptr(T_schema&& schema, T_args&&... args); | |||
} | |||
#include "context.inl" |
@@ -21,7 +21,9 @@ namespace cpphibernate | |||
template<typename T_impl, typename T_bool> | |||
struct init_builder< | |||
mp::list<T_impl, T_bool>, | |||
mp::enable_if_t<mp::is_same_v<bool, mp::decay_t<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>>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, bool recreate) | |||
{ return impl.init(recreate); } | |||
@@ -39,15 +41,18 @@ namespace cpphibernate | |||
{ static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::create(...)!"); } | |||
}; | |||
constexpr decltype(auto) create = mp::generic_predicate<create_builder> { }; | |||
#if 0 | |||
template<typename T_impl, typename T_dataset> | |||
struct create_impl<mp::list<T_impl, T_dataset>, void> | |||
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&>()))>>> | |||
{ | |||
static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) | |||
{ return impl.create(dataset); } | |||
}; | |||
#endif | |||
constexpr decltype(auto) create = mp::generic_predicate<create_builder> { }; | |||
/* read_builder */ | |||
template<typename X, typename = void> | |||
@@ -3,7 +3,12 @@ | |||
#include <cpphibernate/config.h> | |||
#ifdef CPPHIBERNATE_HAS_CPPMARIADB | |||
#include <cpphibernate/driver/mariadb/driver.h> | |||
#include "mariadb/classes.h" | |||
#include "mariadb/context.h" | |||
#include "mariadb/driver.h" | |||
#include "mariadb/driver.inl" | |||
#include "mariadb/helper.h" | |||
#include "mariadb/impl.h" | |||
namespace cpphibernate | |||
{ | |||
@@ -1,3 +1,5 @@ | |||
#pragma once | |||
#include "attributes/attributes.h" | |||
#include "attributes/attributes.inl" |
@@ -1,8 +1,8 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <set> | |||
#include "../forward.h" | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -31,7 +31,12 @@ namespace mariadb { | |||
/** | |||
* @brief Set of attributes. | |||
*/ | |||
struct attributes_t; | |||
struct attributes_t : | |||
public std::set<attribute_t> | |||
{ | |||
using base_type = std::set<attribute_t>; | |||
using base_type::base_type; | |||
}; | |||
/** | |||
* @brief Predicate to create attributes set from attributes schema. | |||
@@ -39,5 +44,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_attributes = mp::generic_predicate<__impl::attributes_builder> { }; | |||
} } | |||
#include "attributes.inl" |
@@ -1,12 +1,10 @@ | |||
#pragma once | |||
#include <set> | |||
#include "attributes.h" | |||
#include <cpphibernate/schema/attribute.h> | |||
#include <cpphibernate/schema/attributes.h> | |||
#include "attributes.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -62,13 +60,4 @@ namespace mariadb { | |||
} | |||
/* attributes_t */ | |||
struct attributes_t : | |||
public std::set<attribute_t> | |||
{ | |||
using base_type = std::set<attribute_t>; | |||
using base_type::base_type; | |||
}; | |||
} } |
@@ -2,3 +2,16 @@ | |||
#include "fields/field.h" | |||
#include "fields/fields.h" | |||
#include "fields/field_data.h" | |||
#include "fields/field_foreign_table.h" | |||
#include "fields/field_primary_key.h" | |||
#include "fields/field_simple.h" | |||
#include "fields/field_value.h" | |||
#include "fields/field.inl" | |||
#include "fields/fields.inl" | |||
#include "fields/field_data.inl" | |||
#include "fields/field_foreign_table.inl" | |||
#include "fields/field_primary_key.inl" | |||
#include "fields/field_simple.inl" | |||
#include "fields/field_value.inl" |
@@ -1,14 +1,17 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <cpphibernate/config.h> | |||
#include <cppmariadb.h> | |||
#include "../forward.h" | |||
#include "../attributes.h" | |||
#include "../../types.h" | |||
#include "../attributes/attributes.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
struct table_t; | |||
struct data_context; | |||
struct create_update_context; | |||
/** | |||
* @brief Abstract field class. | |||
*/ | |||
@@ -78,6 +81,53 @@ namespace mariadb { | |||
*/ | |||
std::ostream& print(std::ostream& os) const; | |||
public: | |||
/** | |||
* @brief Get the value of this field from the current dataset. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* | |||
* @return Value of the field from the current dataset. | |||
*/ | |||
virtual value_t get(const data_context& context) const; | |||
/** | |||
* @brief Set a new value of this field in the current dataset. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* @param[in] value Value of the field to assign to the dataset. | |||
*/ | |||
virtual void set(const data_context& context, const value_t& value) const; | |||
/** | |||
* @brief Check if the value that is represented by this field has the default value. | |||
* | |||
* @param[in] context Data context to receive the value of the assigned dataset. | |||
* | |||
* @retval true If the dataset in the context is the default value for this field. | |||
* @retval false If the dataset in the context is not the default value for this field. | |||
*/ | |||
virtual bool is_default(const data_context& context) const; | |||
/** | |||
* @brief Create a new value that is represented by this field. | |||
* | |||
* @param[in] connection Connection to use to create the value. | |||
* | |||
* @return New created value for this field. | |||
*/ | |||
virtual std::string generate_value(::cppmariadb::connection& connection) const; | |||
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. | |||
*/ | |||
virtual value_t foreign_create_update(const create_update_context& context) const; | |||
private: | |||
/** | |||
* @brief Initialize the field. | |||
@@ -85,6 +135,8 @@ namespace mariadb { | |||
void init(); | |||
}; | |||
using field_ptr_u = std::unique_ptr<const field_t>; | |||
namespace __impl | |||
{ | |||
@@ -102,5 +154,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_field = mp::generic_predicate<__impl::field_builder> { }; | |||
} } | |||
#include "field.inl" |
@@ -1,13 +1,9 @@ | |||
#pragma once | |||
#include <cpphibernate/schema/field.h> | |||
#include <cpphibernate/schema/table.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include "field.h" | |||
#include "field_data.h" | |||
#include "field_primary_key.h" | |||
#include "field_foreign_table.h" | |||
#include <cpphibernate/schema/schema.h> | |||
#include <cpphibernate/misc/type_helper.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,5 +35,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "field_data.inl" |
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#include "field_simple.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,5 +35,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "field_foreign_table.inl" |
@@ -1,6 +1,5 @@ | |||
#pragma once | |||
#include "field_value.h" | |||
#include "../../helper/key_properties.h" | |||
namespace cpphibernate { | |||
@@ -37,8 +36,26 @@ namespace mariadb { | |||
const T_schema& p_schema, | |||
const T_table& p_table, | |||
const T_field& p_field); | |||
public: | |||
/** | |||
* @brief Check if the value that is represented by this field has the default value. | |||
* | |||
* @param[in] context Data context to receive the value of the assigned dataset. | |||
* | |||
* @retval true If the dataset in the context is the default value for this field. | |||
* @retval false If the dataset in the context is not the default value for this field. | |||
*/ | |||
bool is_default(const data_context& context) const override; | |||
/** | |||
* @brief Create a new value that is represented by this field. | |||
* | |||
* @param[in] connection Connection to use to create the value. | |||
* | |||
* @return New created value for this field. | |||
*/ | |||
std::string generate_value(::cppmariadb::connection& connection) const override; | |||
}; | |||
} } | |||
#include "field_primary_key.inl" |
@@ -2,6 +2,8 @@ | |||
#include "field_primary_key.h" | |||
#include "../../context/data_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -30,4 +32,30 @@ namespace mariadb { | |||
this->name = this->table.name + '_' + this->name; | |||
} | |||
template< | |||
typename T_field> | |||
bool field_primary_key_t<T_field> | |||
::is_default(const data_context& context) const | |||
{ | |||
using dataset_type = typename mp::decay_t<T_field>::dataset_type; | |||
auto& dataset = context.get<dataset_type>(); | |||
return key_props.is_default(this->_field.getter(dataset)); | |||
} | |||
template< | |||
typename T_field> | |||
std::string field_primary_key_t<T_field> | |||
::generate_value(::cppmariadb::connection& connection) const | |||
{ | |||
auto * query = key_props.create_key_query(); | |||
if (!query) | |||
throw exception("unable to generate key value: no query defined for this key type!"); | |||
auto ret = connection.execute_used(query); | |||
if (!ret || !ret->next()) | |||
throw exception("unable to generate key value: empty result!"); | |||
return ret->current()->at(0).template get<std::string>(); | |||
} | |||
} } |
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#include "field.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -14,6 +14,10 @@ namespace mariadb { | |||
{ | |||
private: | |||
using base_type = field_t; | |||
using field_type = T_field; | |||
protected: | |||
const field_type& _field; | |||
public: | |||
/** | |||
@@ -35,5 +39,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "field_simple.inl" |
@@ -22,6 +22,7 @@ namespace mariadb { | |||
p_schema, | |||
p_table, | |||
p_field) | |||
, _field(p_field) | |||
{ } | |||
} } |
@@ -41,8 +41,23 @@ namespace mariadb { | |||
const T_schema& p_schema, | |||
const T_table& p_table, | |||
const T_field& p_field); | |||
/** | |||
* @brief Get the value of this field from the current dataset. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* | |||
* @return Value of the field from the current dataset. | |||
*/ | |||
virtual value_t get(const data_context& context) const; | |||
/** | |||
* @brief Set a new value of this field in the current dataset. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* @param[in] value Value of the field to assign to the dataset. | |||
*/ | |||
virtual void set(const data_context& context, const value_t& value) const; | |||
}; | |||
} } | |||
#include "field_value.inl" |
@@ -34,4 +34,22 @@ namespace mariadb { | |||
this->convert_from_close = value_props.convert_from_close() + this->convert_from_close; | |||
} | |||
template<typename T_field> | |||
value_t field_value_t<T_field>:: | |||
get(const data_context& context) const | |||
{ | |||
using dataset_type = typename mp::decay_t<T_field>::dataset_type; | |||
auto& dataset = context.get<dataset_type>(); | |||
return value_props.convert_from(this->_field.getter(dataset)); | |||
} | |||
template<typename T_field> | |||
void field_value_t<T_field> | |||
::set(const data_context& context, const value_t& value) const | |||
{ | |||
using dataset_type = typename mp::decay_t<T_field>::dataset_type; | |||
auto& dataset = context.get<dataset_type>(); | |||
this->_field.setter(dataset, value_props.convert_to(value)); | |||
} | |||
} } |
@@ -1,9 +1,8 @@ | |||
#pragma once | |||
#include <vector> | |||
#include "../forward.h" | |||
#include "field.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,5 +34,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_fields = mp::generic_predicate<__impl::fields_builder> { }; | |||
} } | |||
#include "fields.inl" |
@@ -1,10 +1,6 @@ | |||
#pragma once | |||
#include <cpphibernate/schema/table.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include "field.h" | |||
#include "fields.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,28 +0,0 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
enum init_stage | |||
{ | |||
unknown = 0, | |||
stage1, | |||
stage2, | |||
}; | |||
enum class attribute_t; | |||
struct attributes_t; | |||
struct field_t; | |||
struct fields_t; | |||
struct table_t; | |||
struct tables_t; | |||
struct schema_t; | |||
using field_ptr_u = std::unique_ptr<const field_t>; | |||
using table_ptr_u = std::unique_ptr<const table_t>; | |||
using schema_ptr_u = std::unique_ptr<schema_t>; | |||
} } |
@@ -1,3 +1,5 @@ | |||
#pragma once | |||
#include "schema/schema.h" | |||
#include "schema/schema.inl" |
@@ -1,10 +1,9 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <map> | |||
#include "../forward.h" | |||
#include "../tables/table.h" | |||
#include "../tables/tables.h" | |||
#include "../../context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -16,13 +15,15 @@ namespace mariadb { | |||
{ | |||
public: | |||
using table_map = std::map<size_t, const table_t *>; | |||
using field_map = std::map<size_t, const field_t *>; | |||
private: | |||
table_map _lookup; //!< dataset id to table pointer map | |||
table_map _table_lookup; //!< dataset id to table pointer map | |||
field_map _field_lookup; //!< field id to field pointer map | |||
public: | |||
std::string name; //!< name of the schema | |||
tables_t tables; //!< tables that are managed by this schema. | |||
std::string name; //!< name of the schema | |||
tables_t tables; //!< tables that are managed by this schema. | |||
public: | |||
/** | |||
@@ -58,6 +59,16 @@ namespace mariadb { | |||
*/ | |||
void init(const init_context& context) const; | |||
/** | |||
* @brief Find the table for the passed dataset id in the schema. | |||
*/ | |||
const table_t& table(size_t dataset_id) const; | |||
/** | |||
* @brief Find the field for the passed field id in the schema. | |||
*/ | |||
const field_t& field(size_t field_id) const; | |||
private: | |||
/** | |||
* @brief Initialize the field. | |||
@@ -65,6 +76,8 @@ namespace mariadb { | |||
void init(); | |||
}; | |||
using schema_ptr_u = std::unique_ptr<const schema_t>; | |||
namespace __impl | |||
{ | |||
@@ -82,5 +95,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_schema = mp::generic_predicate<__impl::schema_builder> { }; | |||
} } | |||
#include "schema.inl" |
@@ -1,9 +1,10 @@ | |||
#pragma once | |||
#include <cpphibernate/schema/schema.h> | |||
#include "schema.h" | |||
#include <cpphibernate/types.inl> | |||
#include "../tables/tables.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,4 +1 @@ | |||
#pragma once | |||
#include "tables/table.h" | |||
#include "tables/tables.h" |
@@ -1,16 +1,27 @@ | |||
#pragma once | |||
#include <string> | |||
#include <vector> | |||
#include <memory> | |||
#include <cpphibernate/config.h> | |||
#include <cppmariadb.h> | |||
#include "../forward.h" | |||
#include "../fields/field.h" | |||
#include "../fields/fields.h" | |||
#include "../../context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
struct filter_t; | |||
struct schema_t; | |||
struct init_context; | |||
struct create_update_context; | |||
enum init_stage | |||
{ | |||
unknown = 0, | |||
stage1, | |||
stage2, | |||
}; | |||
/** | |||
* @brief Abstract table class. | |||
*/ | |||
@@ -20,10 +31,6 @@ 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 field_map = std::map<size_t, const field_t *>; //!< map of field id to field | |||
private: | |||
field_map _lookup; //!< field id to field pointer map | |||
public: | |||
size_t id { 0 }; //!< unique id of the table assigned by the user | |||
@@ -88,6 +95,7 @@ namespace mariadb { | |||
*/ | |||
std::ostream& print(std::ostream& os) const; | |||
public: | |||
/** | |||
* @brief Initialize the table using the passed context. | |||
* | |||
@@ -95,21 +103,68 @@ namespace mariadb { | |||
* table is created. In the second stage the table contraints are added. | |||
* The first stage must be completed for all tables before stage two of | |||
* any table is executed. | |||
* | |||
* @param[in] context Context that stores the needed data for the operation. | |||
* @param[in] stage Stage to execute. | |||
*/ | |||
void init(const init_context& context, init_stage stage) const; | |||
private: | |||
/** | |||
* @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. | |||
*/ | |||
virtual std::string create_update(const create_update_context& context) const; | |||
public: | |||
/** | |||
* @brief Get the value of the primary key of this table. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* | |||
* @return Value of the primary key. | |||
*/ | |||
std::string get_primary_key(const data_context& context) const; | |||
/** | |||
* @brief Get the value of the primary key of this table, | |||
* by fetching it from the base table. | |||
* | |||
* @param[in] context Data context to get the dataset from. | |||
* | |||
* @return Value of the primary key. | |||
*/ | |||
std::string get_key_from_base(const data_context& context) const; | |||
/** | |||
* @brief Initialize the field. | |||
* @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. | |||
*/ | |||
void init(); | |||
std::string create_update_exec(const create_update_context& context) const; | |||
private: | |||
using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; | |||
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_init_stage1; //!< Statement for init stage 1 (create table). | |||
mutable statement_ptr_u _statement_init_stage2; //!< Statement for init stage 2 (alter table). | |||
/** | |||
* @brief Get the statement to fetch the key of this table from the base table. | |||
*/ | |||
::cppmariadb::statement& get_statement_key_from_base() const; | |||
/** | |||
* @brief Get or create the mariadb statement for init stage 1. | |||
@@ -120,8 +175,23 @@ namespace mariadb { | |||
* @brief Get or create the mariadb statement for init stage 2. | |||
*/ | |||
::cppmariadb::statement* get_statement_init_stage2() const; | |||
/** | |||
* 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 the update operation. If the statement is empty nullptr is returned; | |||
* | |||
* @param[in] filter Filter to use for the update statement. | |||
* @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; | |||
}; | |||
using table_ptr_u = std::unique_ptr<const table_t>; | |||
namespace __impl | |||
{ | |||
@@ -139,5 +209,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_table = mp::generic_predicate<__impl::table_builder> { }; | |||
} } | |||
#include "table.inl" |
@@ -1,11 +1,11 @@ | |||
#pragma once | |||
#include <cpphibernate/schema/table.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include <cpphibernate/schema/schema.inl> | |||
#include "table.h" | |||
#include "table_simple.h" | |||
#include "table_polymorphic.h" | |||
#include "table_simple.inl" | |||
#include "table_polymorphic.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -24,7 +24,7 @@ namespace mariadb { | |||
, name (p_table.name) | |||
, schema (p_owner) | |||
, fields (make_fields(*this, p_schema, p_table)) | |||
{ init(); } | |||
{ } | |||
namespace __impl | |||
{ | |||
@@ -1,8 +1,8 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include "table.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,5 +35,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "table_polymorphic.inl" |
@@ -1,8 +1,8 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include "table.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -35,5 +35,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "table_simple.inl" |
@@ -1,9 +1,5 @@ | |||
#pragma once | |||
#include <vector> | |||
#include "table.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -34,5 +30,3 @@ namespace mariadb { | |||
constexpr decltype(auto) make_tables = mp::generic_predicate<__impl::tables_builder> { }; | |||
} } | |||
#include "tables.inl" |
@@ -1,10 +1,9 @@ | |||
#pragma once | |||
#include <cpphibernate/schema/tables.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include "tables.h" | |||
#include "table.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,4 +1,11 @@ | |||
#pragma once | |||
#include "context/base_context.h" | |||
#include "context/create_update_context.h" | |||
#include "context/data_context.h" | |||
#include "context/init_context.h" | |||
#include "context/base_context.inl" | |||
#include "context/create_update_context.inl" | |||
#include "context/data_context.inl" | |||
#include "context/init_context.inl" |
@@ -1,7 +1,6 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -25,5 +24,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "base_context.inl" |
@@ -0,0 +1,71 @@ | |||
#pragma once | |||
#include "data_context.h" | |||
#include "../impl/filter.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Context that is used for create or update operations. | |||
*/ | |||
struct create_update_context | |||
: public data_context | |||
{ | |||
const filter_t * filter; //!< Filter that is used for update operation. | |||
//!< Also indicated if this is an update or create operation. | |||
const table_t * derived_table; //!< Derived table if the current context is executed in a base table. | |||
const field_t * owner_field; //!< Field the current dataset is owned by (for foreign fields) | |||
std::string owner_key; //!< Key of the dataset the current dataset is owned by. | |||
ssize_t index; //!< Index of the current dataset in the container. | |||
/** | |||
* @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_dataset Dataset to store in the context. | |||
*/ | |||
template<typename T_dataset> | |||
inline create_update_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset); | |||
/** | |||
* @brief Constructor. Create an update context. | |||
* | |||
* @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. | |||
* @param[in] p_filter Filters to use for the update operation. | |||
*/ | |||
template<typename T_dataset> | |||
inline create_update_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter); | |||
/** | |||
* @brief Create a create context from the current context. | |||
*/ | |||
inline decltype(auto) make_create_context() const; | |||
/** | |||
* @brief Create an update context from the current context. | |||
*/ | |||
inline decltype(auto) make_update_context(const filter_t& p_filter) const; | |||
/** | |||
* @brief Returns true if this is an create context, false otherwise. | |||
*/ | |||
inline bool is_create() const; | |||
/** | |||
* @brief Returns true if this is an update context, false otherwise. | |||
*/ | |||
inline bool is_update() const; | |||
}; | |||
} } |
@@ -0,0 +1,57 @@ | |||
#pragma once | |||
#include "create_update_context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* create_update_context */ | |||
template<typename T_dataset> | |||
create_update_context::create_update_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset) | |||
: data_context (p_schema, p_connection, p_dataset) | |||
, filter (nullptr) | |||
, derived_table (nullptr) | |||
, owner_field (nullptr) | |||
, owner_key () | |||
, index (0) | |||
{ } | |||
template<typename T_dataset> | |||
create_update_context::create_update_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
T_dataset& p_dataset, | |||
const filter_t& p_filter) | |||
: data_context (p_schema, p_connection, p_dataset) | |||
, filter (&p_filter) | |||
, derived_table (nullptr) | |||
, owner_field (nullptr) | |||
, owner_key () | |||
, index (0) | |||
{ } | |||
decltype(auto) create_update_context::make_create_context() const | |||
{ | |||
auto ret = *this; | |||
ret.filter = nullptr; | |||
return ret; | |||
} | |||
decltype(auto) create_update_context::make_update_context(const filter_t& p_filter) const | |||
{ | |||
auto ret = *this; | |||
ret.filter = &p_filter; | |||
return ret; | |||
} | |||
bool create_update_context::is_create() const | |||
{ return !static_cast<bool>(filter); } | |||
bool create_update_context::is_update() const | |||
{ return static_cast<bool>(filter); } | |||
} } |
@@ -0,0 +1,67 @@ | |||
#pragma once | |||
#include "base_context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
namespace __impl | |||
{ | |||
/** | |||
* @brief Helper class to create the change_context predicate. | |||
*/ | |||
template<typename X, typename = void> | |||
struct change_context_builder; | |||
} | |||
/** | |||
* @brief Predicate to change the stored dataset of any data_context. | |||
*/ | |||
constexpr decltype(auto) change_context = cppmp::generic_predicate<__impl::change_context_builder> { }; | |||
/** | |||
* @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 | |||
template<typename X, typename T_enable> | |||
friend struct __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); | |||
}; | |||
} } |
@@ -0,0 +1,99 @@ | |||
#pragma once | |||
#include "data_context.h" | |||
#include "../classes/schema/schema.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* 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 exception("no data assigned!"); | |||
auto dataset_id = 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 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 exception(cppcore::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 = 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; | |||
} | |||
}; | |||
} | |||
} } |
@@ -1,7 +1,5 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
#include "base_context.h" | |||
namespace cpphibernate { | |||
@@ -22,5 +20,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "init_context.inl" |
@@ -2,6 +2,8 @@ | |||
#include "init_context.h" | |||
#include "base_context.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,8 +1,5 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include "classes.h" | |||
#include "impl/driver_impl.h" | |||
namespace cpphibernate { | |||
@@ -23,10 +20,21 @@ namespace mariadb { | |||
public: | |||
/** | |||
* @brief Constructor. Initializes the driver with the passed schema. | |||
* | |||
* @param[in] p_schema Schema to use with this driver. | |||
*/ | |||
template<typename T_schema> | |||
inline driver_t(T_schema&& p_schema); | |||
/** | |||
* @brief Constructor. Initializes the driver with the passed schema. | |||
* | |||
* @param[in] p_schema Schema to use with this driver. | |||
* @param[in] p_connection Connection to the mariadb server to use for queries. | |||
*/ | |||
template<typename T_schema> | |||
inline driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection); | |||
/** | |||
* @brief Print the schema of the driver to the passed stream. | |||
*/ | |||
@@ -156,5 +164,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "driver.inl" |
@@ -1,14 +1,21 @@ | |||
#pragma once | |||
#include "driver.h" | |||
#include "impl/driver_impl.inl" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
template<typename T_schema> | |||
driver_t::driver_t(T_schema&& p_schema) | |||
: _impl(*this, std::forward<T_schema>(p_schema)) | |||
: _impl (*this, std::forward<T_schema>(p_schema)) | |||
, _connection (nullptr) | |||
{ } | |||
template<typename T_schema> | |||
driver_t::driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection) | |||
: _impl (*this, std::forward<T_schema>(p_schema)) | |||
, _connection (&p_connection) | |||
{ } | |||
std::ostream& driver_t::print(std::ostream& os) const | |||
@@ -1,7 +1,9 @@ | |||
#pragma once | |||
#include "helper/type_properties.h" | |||
#include "helper/key_properties.h" | |||
#include "helper/nullable_helper.h" | |||
#include "helper/transaction_lock.h" | |||
#include "helper/type_properties.h" | |||
#include "helper/key_properties.inl" | |||
#include "helper/nullable_helper.inl" | |||
#include "helper/type_properties.inl" |
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -37,5 +37,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "key_properties.inl" |
@@ -1,8 +1,8 @@ | |||
#pragma once | |||
#include "key_properties.h" | |||
#include <cpphibernate/types.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -31,5 +31,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "nullable_helper.inl" |
@@ -1,8 +1,8 @@ | |||
#include "nullable_helper.h" | |||
#include <memory> | |||
#include <cppcore/misc/nullable.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,16 +1,10 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <cppcore/misc/nullable.h> | |||
#include <memory> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Value received from the database. | |||
*/ | |||
using value_t = cppcore::nullable<std::string>; | |||
/** | |||
* @brief Type properties for the passed type. | |||
* | |||
@@ -56,5 +50,3 @@ namespace mariadb { | |||
}; | |||
} } | |||
#include "type_properties.inl" |
@@ -1,10 +1,6 @@ | |||
#pragma once | |||
#include <cpphibernate/types.h> | |||
#include <cppcore/conversion/string.h> | |||
#include "type_properties.h" | |||
#include "nullable_helper.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -1,10 +1,9 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/impl/create_update.h> | |||
#include <cpphibernate/driver/mariadb/impl/destroy.h> | |||
#include <cpphibernate/driver/mariadb/impl/limit.h> | |||
#include <cpphibernate/driver/mariadb/impl/order_by.h> | |||
#include <cpphibernate/driver/mariadb/impl/read.h> | |||
#include <cpphibernate/driver/mariadb/impl/where.h> | |||
#include "impl/create_update.h" | |||
#include "impl/driver_impl.h" | |||
#include "impl/filter.h" | |||
#include <cpphibernate/driver/mariadb/helper/context.inl> | |||
#include "impl/create_update.inl" | |||
#include "impl/driver_impl.inl" | |||
#include "impl/filter.inl" |
@@ -1,8 +1,10 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
#include "../classes.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -17,6 +19,9 @@ namespace mariadb { | |||
/** | |||
* @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, | |||
@@ -29,87 +34,88 @@ namespace mariadb { | |||
struct init_context | |||
: public base_context | |||
{ | |||
bool recreate; | |||
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); | |||
}; | |||
#if 0 | |||
/* init_context */ | |||
struct init_context | |||
/** | |||
* @brief Mariadb driver context that helds a specific dataset. | |||
*/ | |||
struct data_context | |||
: public base_context | |||
{ | |||
bool recreate; | |||
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 | |||
inline init_context( | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection, | |||
bool p_recreate) | |||
: base_context (p_schema, p_connection) | |||
, recreate (p_recreate) | |||
{ } | |||
}; | |||
friend __impl::change_context_builder; | |||
namespace __impl | |||
{ | |||
struct change_context_impl | |||
{ | |||
template<typename T_context, typename T_data> | |||
constexpr decltype(auto) operator()(const T_context& context, T_data& data) const | |||
{ | |||
auto new_context = context; | |||
new_context.set(data); | |||
return new_context; | |||
} | |||
}; | |||
} | |||
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; | |||
constexpr decltype(auto) change_context = __impl::change_context_impl { }; | |||
private: | |||
/** | |||
* @brief Set the dataset that is stored in this context. | |||
*/ | |||
template<typename T_dataset> | |||
inline void set(T_dataset& dataset); | |||
}; | |||
/* data_context */ | |||
struct data_context | |||
: public base_context | |||
namespace __impl | |||
{ | |||
private: | |||
friend __impl::change_context_impl; | |||
mutable size_t _dataset_id; | |||
mutable void* _dataset; | |||
mutable const table_t* _table; | |||
/** | |||
* @brief Helper class to create the change_context predicate. | |||
*/ | |||
template<typename X, typename = void> | |||
struct change_context_builder; | |||
protected: | |||
template<typename T_dataset> | |||
inline void* set(T_dataset& dataset, size_t dataset_id = 0) const; | |||
} | |||
public: | |||
template<typename T_dataset> | |||
inline decltype(auto) get() const; | |||
/** | |||
* @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> { }; | |||
inline 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_data> | |||
inline data_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection) | |||
: base_context (p_schema, p_connection) | |||
, _dataset_id (misc::get_type_id(hana::type_c<mp::decay_t<T_data>>)) | |||
, _dataset (&p_data) | |||
, _table (nullptr) | |||
{ } | |||
}; | |||
#if 0 | |||
/* filter_context */ | |||
@@ -1,6 +1,6 @@ | |||
#pragma once | |||
#include "context.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -14,57 +14,99 @@ namespace mariadb { | |||
, connection(p_connection) | |||
{ } | |||
#if 0 | |||
/* data_context */ | |||
template<typename T_dataset> | |||
inline void* data_context | |||
::set(T_dataset& dataset, size_t dataset_id) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
_table = nullptr; | |||
_dataset = &dataset; | |||
_dataset_id = (dataset_id == 0) | |||
? misc::get_type_id(hana::type_c<dataset_type>) | |||
: dataset_id; | |||
return _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> | |||
inline decltype(auto) data_context | |||
::get() const | |||
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) | |||
{ | |||
/* check table */ | |||
/* 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> |
@@ -1,10 +1,10 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -93,4 +93,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -1,8 +1,8 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier.h> | |||
#include <cpphibernate/driver/mariadb/impl/modifier_tags.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -1,7 +1,7 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -1,9 +1,9 @@ | |||
#pragma once | |||
#include <sstream> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -1,10 +1,10 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -262,4 +262,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { }; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -1,9 +1,9 @@ | |||
#pragma once | |||
#include <sstream> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/modifier.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
@@ -0,0 +1,14 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Helper class to select the right implementation of the create/update operation for the passed datatype. | |||
* | |||
* @tparam T_dataset Dataset type to select implementation for. | |||
*/ | |||
template<typename T_dataset, typename = void> | |||
struct create_update_impl_t; | |||
} } |
@@ -1,47 +1,54 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* create_update_impl_t */ | |||
template<typename T_dataset, typename = void> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/* create_update_impl_t - default (normal dataset) */ | |||
template<typename T_dataset, typename> | |||
struct create_update_impl_t | |||
{ | |||
using dataset_type = T_dataset; | |||
static inline value_t apply(const create_update_context& context, bool strict = true) | |||
{ | |||
using namespace ::cppmariadb; | |||
value_t ret; | |||
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); | |||
if (table.primary_key_field->is_default(context) == context.is_update) | |||
/* if the value of primary key field does not match the operation of the context */ | |||
if (table.primary_key_field->is_default(context) != context.is_create()) | |||
{ | |||
/* if we are not in strict mode, change the context to an update context */ | |||
if (!strict) | |||
{ | |||
auto update_context = context; | |||
update_context.is_update = !update_context.is_update; | |||
ret = table.create_update(update_context); | |||
static const filter_t dummy; | |||
ret = table.create_update(context.make_update_context(dummy)); | |||
} | |||
else if (context.is_update) | |||
/* if we expect an update operation in strict mode throw an exception | |||
* because an update operation needs an primary key assigned */ | |||
else if (context.is_update()) | |||
{ | |||
throw misc::hibernate_exception("can not update dataset with no primary key assigned!"); | |||
throw exception("can not update dataset with no primary key assigned!"); | |||
} | |||
/* if we expect an create operation in strict mode throw an exception | |||
because an create operation expects the primary key to not be assigned */ | |||
else | |||
{ | |||
throw misc::hibernate_exception("can not create dataset with primary key assigned!"); | |||
throw exception("can not create dataset with primary key assigned!"); | |||
} | |||
} | |||
else | |||
@@ -58,10 +65,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_dataset> | |||
struct create_update_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 value_t apply(const create_update_context& context, bool strict = true) | |||
{ | |||
@@ -77,7 +84,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
else if (strict) | |||
{ | |||
throw misc::hibernate_exception("can not create nullable type with no value!"); | |||
throw exception("can not create nullable type with no value!"); | |||
} | |||
return ret; | |||
} | |||
@@ -88,12 +95,14 @@ beg_namespace_cpphibernate_driver_mariadb | |||
template<typename T_dataset> | |||
struct create_update_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; | |||
static inline value_t apply(const create_update_context& context, bool strict = true) | |||
{ | |||
using namespace ::cppmariadb; | |||
value_t ret; | |||
auto& connection = context.connection; | |||
auto& dataset = context.get<dataset_type>(); | |||
@@ -113,5 +122,4 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb | |||
} } |
@@ -1,8 +1,6 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include "../classes.h" | |||
#include "../classes/schema/schema.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -36,6 +34,15 @@ namespace mariadb { | |||
*/ | |||
inline void init(bool recreate) const; | |||
/** | |||
* @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. | |||
*/ | |||
template<typename T_dataset> | |||
inline void create(T_dataset& dataset) const; | |||
/* | |||
} | |||
@@ -1,7 +1,8 @@ | |||
#pragma once | |||
#include "driver_impl.h" | |||
#include "../driver.h" | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
@@ -24,4 +25,14 @@ namespace mariadb { | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
void driver_impl_t::create(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)); | |||
} | |||
} } |
@@ -0,0 +1,60 @@ | |||
#pragma once | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
struct field_t; | |||
struct table_t; | |||
struct schema_t; | |||
/** | |||
* @brief Inclusive or exclusive field and table filter. | |||
* Is used in update and read operations to select the fields that should be updated / fetched. | |||
*/ | |||
struct filter_t | |||
{ | |||
public: | |||
using field_set_type = std::set<const field_t *>; | |||
using table_set_type = std::set<const table_t *>; | |||
ssize_t cache_id { 0 }; //!< Unique ID that indicates the current filtered tables and fields | |||
bool exclusive { true }; //!< True: Use exclusive filter. False: Use inclusive filter. | |||
field_set_type fields; //!< Set of fields assigned to the filter. | |||
table_set_type tables; //!< Set of tables assigned to the filter. | |||
public: | |||
/** | |||
* @brief Returns true if the passed table is excluded. | |||
*/ | |||
inline bool is_excluded(const table_t& table) const; | |||
/** | |||
* @brief Returns true if the passed field is excluded. | |||
*/ | |||
inline bool is_excluded(const field_t& field) const; | |||
/** | |||
* @brief Set included fields and tables. | |||
* | |||
* @param[in] schema Mariadb driver schame to use for looking up fields and tables. | |||
* @param[in] args Fields and tables from the cpphibernate schema to include in this filter. | |||
*/ | |||
template<typename... T_args> | |||
inline void set_inclusive(const schema_t& schema, T_args&&... args); | |||
/** | |||
* @brief Set excluded fields and tables. | |||
* | |||
* @param[in] schema Mariadb driver schame to use for looking up fields and tables. | |||
* @param[in] args Fields and tables from the cpphibernate schema to exclude in this filter. | |||
*/ | |||
template<typename... T_args> | |||
inline void set_exclusive(const schema_t& schema, T_args&&... args); | |||
/** | |||
* @brief Clear the filter. | |||
*/ | |||
inline void clear(); | |||
}; | |||
} } |
@@ -0,0 +1,131 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include "filter.h" | |||
#include "../classes/fields/field.h" | |||
#include "../classes/tables/table.h" | |||
#include "../classes/schema/schema.h" | |||
#include <cpphibernate/schema/field.inl> | |||
#include <cpphibernate/schema/table.inl> | |||
#include <cpphibernate/schema/schema.inl> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
namespace __impl | |||
{ | |||
/* filter_add_element_impl */ | |||
template<typename X, typename = void> | |||
struct filter_add_element_impl | |||
{ | |||
template<typename... T_args> | |||
static constexpr decltype(auto) apply(T_args&&... args) | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for filter_add_element(...)!"); } | |||
}; | |||
template<typename T_table> | |||
struct filter_add_element_impl< | |||
mp::list<filter_t&, const schema_t&, T_table>, | |||
mp::enable_if_t< | |||
schema::is_table_v<mp::decay_t<T_table>>>> | |||
{ | |||
static constexpr decltype(auto) apply(filter_t& filter, const schema_t& schema, const T_table& table) | |||
{ | |||
auto dataset_id = get_type_id(table.wrapped_dataset); | |||
auto& t = schema.table(dataset_id); | |||
filter.tables.emplace(&t); | |||
for (auto& ptr : t.fields) | |||
{ | |||
filter.fields.emplace(ptr.get()); | |||
} | |||
} | |||
}; | |||
template<typename T_field> | |||
struct filter_add_element_impl< | |||
mp::list<filter_t&, const schema_t&, T_field>, | |||
mp::enable_if_t< | |||
schema::is_field_v<mp::decay_t<T_field>>>> | |||
{ | |||
static constexpr decltype(auto) apply(filter_t& filter, const schema_t& schema, const T_field& field) | |||
{ | |||
auto field_id = get_type_id(hana::type_c<mp::decay_t<T_field>>); | |||
auto& f = schema.field(field_id); | |||
filter.fields.emplace(&f); | |||
filter.tables.emplace(f.table); | |||
} | |||
}; | |||
constexpr decltype(auto) filter_add_element = ::cppmp::generic_predicate<__impl::filter_add_element_impl> { }; | |||
} | |||
/* filter_t */ | |||
bool filter_t::is_excluded(const table_t& table) const | |||
{ | |||
auto ret = static_cast<bool>(tables.count(&table)); | |||
if (!exclusive) | |||
ret = !ret; | |||
return ret; | |||
} | |||
bool filter_t::is_excluded(const field_t& field) const | |||
{ | |||
auto ret = static_cast<bool>(fields.count(&field)); | |||
if (!exclusive) | |||
ret = !ret; | |||
return ret; | |||
} | |||
template<typename... T_args> | |||
void filter_t::set_inclusive(const schema_t& schema, T_args&&... args) | |||
{ | |||
clear(); | |||
exclusive = false; | |||
cache_id = static_cast<ssize_t>(cppcore::get_unique_id<filter_t, mp::decay_t<T_args>...>() + 1); | |||
int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward<T_args>(args)), void(), 0)... }; | |||
(void)dummy; | |||
} | |||
template<typename... T_args> | |||
void filter_t::set_exclusive(const schema_t& schema, T_args&&... args) | |||
{ | |||
clear(); | |||
exclusive = true; | |||
cache_id = -static_cast<ssize_t>(cppcore::get_unique_id<filter_t, mp::decay_t<T_args>...>() + 1); | |||
int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward<T_args>(args)), void(), 0)... }; | |||
(void)dummy; | |||
// remove excluded tables if not all fields are excluded | |||
auto it = tables.begin(); | |||
while (it != tables.end()) | |||
{ | |||
bool removed = false; | |||
for (auto& field : (*it)->fields) | |||
{ | |||
if (fields.count(field.get())) | |||
{ | |||
it = tables.erase(it); | |||
removed = true; | |||
break; | |||
} | |||
} | |||
if (!removed) | |||
++it; | |||
} | |||
} | |||
void filter_t::clear() | |||
{ | |||
cache_id = 0; | |||
exclusive = true; | |||
fields.clear(); | |||
tables.clear(); | |||
} | |||
} } |
@@ -0,0 +1,13 @@ | |||
#pragma once | |||
#include <cppcore/misc/nullable.h> | |||
namespace cpphibernate { | |||
namespace mariadb { | |||
/** | |||
* @brief Value received from the database. | |||
*/ | |||
using value_t = cppcore::nullable<std::string>; | |||
} } |
@@ -4,3 +4,6 @@ | |||
#include "misc/print_container.h" | |||
#include "misc/printing.h" | |||
#include "misc/type_helper.h" | |||
#include "misc/print_container.inl" | |||
#include "misc/type_helper.inl" |
@@ -1,5 +1,7 @@ | |||
#pragma once | |||
#include "modifier/modifier.h" | |||
#include "modifier/modifiers.h" | |||
@@ -16,3 +18,22 @@ | |||
#include "modifier/where/negation.h" | |||
#include "modifier/where/conjunction.h" | |||
#include "modifier/where/disjunction.h" | |||
#include "modifier/modifier.inl" | |||
#include "modifier/modifiers.inl" | |||
#include "modifier/limit.inl" | |||
#include "modifier/offset.inl" | |||
#include "modifier/order_by.inl" | |||
#include "modifier/order_by/ascending.inl" | |||
#include "modifier/order_by/descending.inl" | |||
#include "modifier/where.inl" | |||
#include "modifier/where/where_clause.inl" | |||
#include "modifier/where/equal.inl" | |||
#include "modifier/where/negation.inl" | |||
#include "modifier/where/conjunction.inl" | |||
#include "modifier/where/disjunction.inl" |
@@ -9,3 +9,11 @@ | |||
#include "schema/table.h" | |||
#include "schema/tables.h" | |||
#include "schema/schema.h" | |||
#include "schema/attribute.inl" | |||
#include "schema/attributes.inl" | |||
#include "schema/field.inl" | |||
#include "schema/fields.inl" | |||
#include "schema/table.inl" | |||
#include "schema/tables.inl" | |||
#include "schema/schema.inl" |
@@ -2,6 +2,7 @@ | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/misc/type_helper.h> | |||
#include <cpphibernate/misc/equality_compare.h> | |||
namespace cpphibernate { | |||
namespace schema { | |||
@@ -1,6 +1,8 @@ | |||
#pragma once | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/misc/type_helper.h> | |||
#include <cpphibernate/misc/equality_compare.h> | |||
namespace cpphibernate { | |||
namespace schema { | |||
@@ -90,6 +90,16 @@ namespace cpphibernate | |||
*/ | |||
inline uuid(const uuid&) = default; | |||
/** | |||
* @brief Move assignment constructor. | |||
*/ | |||
inline uuid& operator = (uuid&&) = default; | |||
/** | |||
* @brief Copy assignment constructor. | |||
*/ | |||
inline uuid& operator = (const uuid&) = default; | |||
/** | |||
* @brief Write the UUID to passed stream. | |||
* | |||
@@ -1,282 +0,0 @@ | |||
#include <string> | |||
#include <iostream> | |||
#include <cpputils/misc/enum.h> | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/misc/indent.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/driver/mariadb/schema/field.h> | |||
#include <cpphibernate/driver/mariadb/schema/table.h> | |||
using namespace ::std; | |||
using namespace ::utl; | |||
using namespace ::cpphibernate::driver::mariadb_impl; | |||
void field_t::print(std::ostream& os) const | |||
{ | |||
os << indent << '{' | |||
<< incindent | |||
<< indent << "\"id\": " << id << "," | |||
<< indent << "\"dataset_id\": " << dataset_id << "," | |||
<< indent << "\"real_dataset_id\": " << real_dataset_id << "," | |||
<< indent << "\"value_id\": " << value_id << "," | |||
<< indent << "\"real_value_id\": " << real_value_id << "," | |||
<< indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," | |||
<< indent << "\"value_is_pointer\": " << (value_is_pointer ? "true" : "false") << "," | |||
<< indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," | |||
<< indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," | |||
<< indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," | |||
<< indent << "\"referenced_table\": " << (referenced_table ? std::string("\"") + referenced_table->table_name + "\"" : "null") << "," | |||
<< indent << "\"schema_name\": \"" << schema_name << "\"," | |||
<< indent << "\"table_name\": \"" << table_name << "\"," | |||
<< indent << "\"field_name\": \"" << field_name << "\"," | |||
<< indent << "\"type\": \"" << type << "\"," | |||
<< indent << "\"create_arguments\": \"" << create_arguments << "\"," | |||
<< indent << "\"convert_to_open\": \"" << convert_to_open << "\"," | |||
<< indent << "\"convert_to_close\": \"" << convert_to_close << "\"," | |||
<< indent << "\"convert_from_open\": \"" << convert_from_open << "\"," | |||
<< indent << "\"convert_from_close\": \"" << convert_from_close << "\"," | |||
<< indent << "\"attributes\": " << misc::print_container(attributes, false) | |||
<< decindent | |||
<< indent << '}'; | |||
} | |||
void field_t::update() | |||
{ | |||
id = 0; | |||
dataset_id = 0; | |||
real_dataset_id = 0; | |||
value_id = 0; | |||
real_value_id = 0; | |||
value_is_nullable = false; | |||
value_is_container = false; | |||
value_is_auto_incremented = false; | |||
table = nullptr; | |||
referenced_table = nullptr; | |||
type.clear(); | |||
create_arguments.clear(); | |||
/* conver_to_open */ | |||
{ | |||
std::ostringstream ss; | |||
for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
{ | |||
switch(*it) | |||
{ | |||
case attribute_t::hex: | |||
ss << "HEX("; | |||
break; | |||
case attribute_t::compress: | |||
ss << "COMPRESS("; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
convert_to_open = ss.str(); | |||
} | |||
/* convert_to_close */ | |||
{ | |||
std::ostringstream ss; | |||
for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
{ | |||
switch(*it) | |||
{ | |||
case attribute_t::hex: | |||
case attribute_t::compress: | |||
ss << ')'; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
convert_to_close = ss.str(); | |||
} | |||
/* convert_from_open */ | |||
{ | |||
std::ostringstream ss; | |||
for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
{ | |||
switch(*it) | |||
{ | |||
case attribute_t::hex: | |||
ss << "UNHEX("; | |||
break; | |||
case attribute_t::compress: | |||
ss << "UNCOMPRESS("; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
convert_from_open = ss.str(); | |||
} | |||
/* convert_from_close */ | |||
{ | |||
std::ostringstream ss; | |||
for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
{ | |||
switch(*it) | |||
{ | |||
case attribute_t::hex: | |||
case attribute_t::compress: | |||
ss << ')'; | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
convert_from_close = ss.str(); | |||
} | |||
} | |||
#define throw_not_implemented(p_ret, p_name, ...) \ | |||
p_ret field_t::p_name(__VA_ARGS__) const \ | |||
{ \ | |||
throw misc::hibernate_exception( \ | |||
std::string("'") + table_name + "." + field_name + \ | |||
"' does not implement the " #p_name "() method!"); \ | |||
} | |||
/* CRUD */ | |||
throw_not_implemented(value_t, foreign_create_update, const create_update_context&) | |||
throw_not_implemented(read_context_ptr, foreign_read, const read_context&, bool fake_context) | |||
/* properties */ | |||
throw_not_implemented(bool, is_default, const data_context& context) | |||
throw_not_implemented(string, generate_value, ::cppmariadb::connection&) | |||
throw_not_implemented(value_t, get, const data_context& context) | |||
throw_not_implemented(void, set, const data_context& context, const value_t&) | |||
/* statements */ | |||
throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_one_delete, bool) | |||
throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_update) | |||
::cppmariadb::statement& field_t::get_statement_foreign_one_delete_impl(bool key_known, statement_ptr& known, statement_ptr& unknown) const | |||
{ | |||
assert(table); | |||
assert(table->primary_key_field); | |||
assert(referenced_table); | |||
assert(referenced_table->primary_key_field); | |||
if (key_known) | |||
{ | |||
if (!known) | |||
{ | |||
auto& ref_table = *referenced_table; | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "` FROM `" | |||
<< key_info.table_name | |||
<< "` WHERE `" | |||
<< key_info.field_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); | |||
known.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *known; | |||
} | |||
else | |||
{ | |||
if (!unknown) | |||
{ | |||
auto& ref_table = *referenced_table; | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "` FROM `" | |||
<< key_info.table_name | |||
<< "` WHERE `" | |||
<< key_info.field_name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< ")"; | |||
auto where = os.str(); | |||
auto query = ref_table.build_delete_query(&where); | |||
unknown.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *unknown; | |||
} | |||
} | |||
::cppmariadb::statement& field_t::get_statement_foreign_many_update_impl(statement_ptr& statement) const | |||
{ | |||
assert(referenced_table); | |||
assert(referenced_table->primary_key_field); | |||
if (!statement) | |||
{ | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "UPDATE `" | |||
<< ref_key_info.table_name | |||
<< "` SET `" | |||
<< table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "`=NULL"; | |||
if (value_is_container) | |||
{ | |||
os << ", `" | |||
<< table_name | |||
<< "_index_" | |||
<< field_name | |||
<< "`=0"; | |||
} | |||
os << " WHERE `" | |||
<< table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "`=" | |||
<< ref_key_info.convert_to_open | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close; | |||
statement.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *statement; | |||
} |
@@ -1,3 +1,4 @@ | |||
#include <cpphibernate/types.h> | |||
#include <cpphibernate/misc/print_container.h> | |||
#include <cpphibernate/driver/mariadb/classes/fields/field.h> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
@@ -20,9 +21,6 @@ std::ostream& field_t::print(std::ostream& os) const | |||
<< incindent | |||
<< indent << "\"id\": " << id << "," | |||
// TODO | |||
// << indent << "\"dataset_id\": " << dataset_id << "," | |||
// << indent << "\"real_dataset_id\": " << real_dataset_id << "," | |||
<< indent << "\"value_id\": " << value_id << "," | |||
<< indent << "\"real_value_id\": " << real_value_id << "," | |||
<< indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," | |||
@@ -33,11 +31,6 @@ std::ostream& field_t::print(std::ostream& os) const | |||
<< indent << "\"referenced_table\": " << (referenced_table | |||
? std::string("\"") + referenced_table->name + "\"" | |||
: "null") << "," | |||
// TODO | |||
// << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
// << indent << "\"table_name\": \"" << table_name << "\"," | |||
// << indent << "\"field_name\": \"" << field_name << "\"," | |||
<< indent << "\"name\": \"" << name << "\"," | |||
<< indent << "\"type\": \"" << type << "\"," | |||
<< indent << "\"create_arguments\": \"" << create_arguments << "\"," | |||
@@ -133,3 +126,17 @@ void field_t::init() | |||
convert_from_close = ss.str(); | |||
} | |||
} | |||
#define throw_not_implemented(p_ret, p_name, ...) \ | |||
p_ret field_t::p_name(__VA_ARGS__) const \ | |||
{ \ | |||
throw ::cpphibernate::exception( \ | |||
std::string("'") + table.name + "." + name + \ | |||
"' does not implement the " #p_name "() method!"); \ | |||
} | |||
throw_not_implemented(value_t, get, const data_context& context) | |||
throw_not_implemented(void, set, const data_context& context, const value_t& value) | |||
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) |
@@ -1,259 +0,0 @@ | |||
#include <string> | |||
#include <sstream> | |||
#include <cpputils/misc/enum.h> | |||
#include <cpputils/misc/string.h> | |||
#include <cpputils/misc/indent.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
using namespace ::utl; | |||
using namespace ::cpphibernate::driver::mariadb_impl; | |||
void schema_t::update() | |||
{ | |||
// clear everything | |||
for (auto& kvp : tables) | |||
{ | |||
assert(static_cast<bool>(kvp.second)); | |||
auto& table = *kvp.second; | |||
table.primary_key_field = nullptr; | |||
table.derived_tables.clear(); | |||
table.foreign_key_fields.clear(); | |||
table.foreign_table_fields.clear(); | |||
table.foreign_table_one_fields.clear(); | |||
table.foreign_table_many_fields.clear(); | |||
table.data_fields.clear(); | |||
table.is_used_in_container = false; | |||
for (auto& ptr : table.fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
field.update(); | |||
} | |||
} | |||
// update references | |||
for (auto& kvp : tables) | |||
{ | |||
assert(static_cast<bool>(kvp.second)); | |||
auto& table = *kvp.second; | |||
// base table | |||
auto it = tables.find(table.base_dataset_id); | |||
table.base_table = (it != tables.end() | |||
? it->second.get() | |||
: nullptr); | |||
// derived tables | |||
for (auto& id : table.derived_dataset_ids) | |||
{ | |||
it = tables.find(id); | |||
if (it == tables.end()) | |||
throw misc::hibernate_exception(std::string("unable to find derived table for dataset id ") + std::to_string(id)); | |||
table.derived_tables.emplace_back(it->second.get()); | |||
} | |||
// update fields | |||
for (auto& ptr : table.fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
// table | |||
if (table.dataset_id != field.dataset_id) | |||
throw misc::hibernate_exception(std::string("dataset id of field '") + field.table_name + '.' + field.field_name + "' does not match!"); | |||
field.table = &table; | |||
// referenced table | |||
it = tables.find(field.real_value_id); | |||
auto referenced_table = (it != tables.end() | |||
? it->second.get() | |||
: nullptr); | |||
field.referenced_table = referenced_table; | |||
// is primary key field | |||
if (field.attributes.count(attribute_t::primary_key)) | |||
{ | |||
if (static_cast<bool>(table.primary_key_field)) | |||
throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' can not have more then one primary key!"); | |||
table.primary_key_field = &field; | |||
} | |||
// is foreign table field | |||
else if (static_cast<bool>(referenced_table)) | |||
{ | |||
table.foreign_table_fields.emplace_back(&field); | |||
if (field.value_is_container) | |||
{ | |||
referenced_table->is_used_in_container = true; | |||
} | |||
} | |||
// is data field | |||
else | |||
{ | |||
table.data_fields.emplace_back(&field); | |||
} | |||
} | |||
if (!static_cast<bool>(table.primary_key_field)) | |||
throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' does not have a primary key!"); | |||
} | |||
// update foreign fields (one, many, key) | |||
for (auto& kvp : tables) | |||
{ | |||
assert(static_cast<bool>(kvp.second)); | |||
auto& table = *kvp.second; | |||
for (auto& ptr : table.foreign_table_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
auto& referenced_table = *field.referenced_table; | |||
if (field.value_is_container) | |||
{ | |||
table.foreign_table_many_fields.emplace_back(&field); | |||
referenced_table.foreign_key_fields.push_back(&field); | |||
} | |||
else | |||
{ | |||
table.foreign_table_one_fields.emplace_back(&field); | |||
if (referenced_table.is_used_in_container) | |||
{ | |||
referenced_table.foreign_key_fields.push_back(&field); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
void schema_t::print(std::ostream& os) const | |||
{ | |||
os << indent << '{' | |||
<< incindent | |||
<< indent << "\"schema_name\": \"" << schema_name << "\"," | |||
<< indent << "\"tables\": " << misc::print_container(tables, true, [](auto& s, auto& kvp) { | |||
kvp.second->print(s); | |||
}) | |||
<< decindent | |||
<< indent << '}'; | |||
} | |||
const table_t& schema_t::table(size_t dataset_id) const | |||
{ | |||
auto it = tables.find(dataset_id); | |||
if (it == tables.end()) | |||
throw misc::hibernate_exception(std::string("unable to find table for dataset with id ") + std::to_string(dataset_id)); | |||
assert(static_cast<bool>(it->second)); | |||
return *it->second; | |||
} | |||
const field_t& schema_t::field(size_t field_id) const | |||
{ | |||
for (auto& kvp : tables) | |||
{ | |||
assert(kvp.second); | |||
auto& table = *kvp.second; | |||
for (auto& ptr : table.fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (field.id == field_id) | |||
return field; | |||
} | |||
} | |||
throw misc::hibernate_exception(std::string("unable to find field with id ") + std::to_string(field_id)); | |||
} | |||
#define exec_query() \ | |||
do { \ | |||
cpphibernate_debug_log("execute init query: " << ss.str()); \ | |||
connection.execute(ss.str()); \ | |||
ss.str(std::string()); \ | |||
ss.clear(); \ | |||
} while(0) | |||
void schema_t::init(const init_context& context) const | |||
{ | |||
std::ostringstream ss; | |||
auto& connection = context.connection; | |||
if (context.recreate) | |||
{ | |||
ss << "DROP DATABASE IF EXISTS `" | |||
<< schema_name | |||
<< "`"; | |||
exec_query(); | |||
} | |||
/* create schema */ | |||
ss << "CREATE SCHEMA IF NOT EXISTS `" | |||
<< schema_name | |||
<< "` DEFAULT CHARACTER SET utf8"; | |||
exec_query(); | |||
/* use schema */ | |||
ss << "USE `" | |||
<< schema_name | |||
<< "`"; | |||
exec_query(); | |||
/* UuidToBin */ | |||
ss << "CREATE FUNCTION IF NOT EXISTS UuidToBin(_uuid CHAR(36))\n" | |||
" RETURNS BINARY(16)\n" | |||
" LANGUAGE SQL\n" | |||
" DETERMINISTIC\n" | |||
" CONTAINS SQL\n" | |||
" SQL SECURITY INVOKER\n" | |||
"RETURN\n" | |||
" UNHEX(CONCAT(\n" | |||
" SUBSTR(_uuid, 25, 12),\n" // node id | |||
" SUBSTR(_uuid, 20, 4),\n" // clock sequence | |||
" SUBSTR(_uuid, 15, 4),\n" // time high and version | |||
" SUBSTR(_uuid, 10, 4),\n" // time mid | |||
" SUBSTR(_uuid, 1, 8)\n" // time low | |||
" )\n" | |||
")"; | |||
exec_query(); | |||
/* BinToUuid */ | |||
ss << "CREATE FUNCTION IF NOT EXISTS BinToUuid(_bin BINARY(16))\n" | |||
" RETURNS CHAR(36)\n" | |||
" LANGUAGE SQL\n" | |||
" DETERMINISTIC\n" | |||
" CONTAINS SQL\n" | |||
" SQL SECURITY INVOKER\n" | |||
"RETURN\n" | |||
" IF(\n" | |||
" _bin IS NULL,\n" | |||
" NULL,\n" | |||
" LCASE(CONCAT_WS('-',\n" | |||
" HEX(SUBSTR(_bin, 13, 4)),\n" // time low | |||
" HEX(SUBSTR(_bin, 11, 2)),\n" // time mid | |||
" HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version | |||
" HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence | |||
" HEX(SUBSTR(_bin, 1, 6))\n" // node id | |||
" )\n" | |||
" )\n" | |||
")"; | |||
exec_query(); | |||
/* initialize tables */ | |||
for (auto& kvp : tables) | |||
{ | |||
assert(kvp.second); | |||
kvp.second->init_stage1(context); | |||
} | |||
for (auto& kvp : tables) | |||
{ | |||
assert(kvp.second); | |||
kvp.second->init_stage2(context); | |||
} | |||
} | |||
#undef exec_query |
@@ -1,4 +1,5 @@ | |||
#include <cpphibernate/driver/mariadb/classes/schema/schema.h> | |||
#include <cpphibernate/driver/mariadb/context/init_context.inl> | |||
#include <cpphibernate/driver/mariadb/classes/schema/schema.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
@@ -1,8 +1,9 @@ | |||
#include <cppcore/misc/indent.h> | |||
#include <cpphibernate/types.h> | |||
#include <cpphibernate/misc/print_container.h> | |||
#include <cpphibernate/driver/mariadb/classes/schema/schema.h> | |||
#include <cppcore/misc/indent.h> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
@@ -26,11 +27,17 @@ std::ostream& schema_t::print(std::ostream& os) const | |||
void schema_t::init() | |||
{ | |||
/* build lookup */ | |||
/* build lookup tables */ | |||
for (auto& t : tables) | |||
{ | |||
assert(static_cast<bool>(t)); | |||
_lookup.emplace(t->dataset_id, t.get()); | |||
_table_lookup.emplace(t->dataset_id, t.get()); | |||
for (auto& f : t->fields) | |||
{ | |||
assert(static_cast<bool>(f)); | |||
_field_lookup.emplace(f->id, f.get()); | |||
} | |||
} | |||
/* update table referencs */ | |||
@@ -40,16 +47,16 @@ void schema_t::init() | |||
auto& table = const_cast<table_t&>(*t); | |||
/* get base table */ | |||
auto it = _lookup.find(table.base_dataset_id); | |||
table.base_table = (it != _lookup.end() | |||
auto it = _table_lookup.find(table.base_dataset_id); | |||
table.base_table = (it != _table_lookup.end() | |||
? it->second | |||
: nullptr); | |||
/* dereived tables */ | |||
for (auto& id : table.derived_dataset_ids) | |||
{ | |||
it = _lookup.find(id); | |||
if (it == _lookup.end()) | |||
it = _table_lookup.find(id); | |||
if (it == _table_lookup.end()) | |||
throw exception(std::string("unable to find derived table for dataset id ") + std::to_string(id)); | |||
table.derived_tables.emplace_back(it->second); | |||
} | |||
@@ -61,8 +68,8 @@ void schema_t::init() | |||
auto& field = const_cast<field_t&>(*f); | |||
/* referenced_table */ | |||
it = _lookup.find(field.real_value_id); | |||
auto * referenced_table = (it != _lookup.end() | |||
it = _table_lookup.find(field.real_value_id); | |||
auto * referenced_table = (it != _table_lookup.end() | |||
? const_cast<table_t*>(it->second) | |||
: nullptr); | |||
field.referenced_table = referenced_table; | |||
@@ -125,3 +132,21 @@ void schema_t::init() | |||
} | |||
} | |||
} | |||
const table_t& schema_t::table(size_t dataset_id) const | |||
{ | |||
auto it = _table_lookup.find(dataset_id); | |||
if (it == _table_lookup.end()) | |||
throw exception(std::string("unable to find table for dataset with id ") + std::to_string(dataset_id)); | |||
assert(static_cast<bool>(it->second)); | |||
return *it->second; | |||
} | |||
const field_t& schema_t::field(size_t field_id) const | |||
{ | |||
auto it = _field_lookup.find(field_id); | |||
if (it == _field_lookup.end()) | |||
throw exception(std::string("unable to find field for dataset with id ") + std::to_string(field_id)); | |||
assert(static_cast<bool>(it->second)); | |||
return *it->second; | |||
} |
@@ -0,0 +1,483 @@ | |||
#include <cpphibernate/driver/mariadb/types.h> | |||
#include <cpphibernate/types.inl> | |||
#include <cpphibernate/driver/mariadb/impl/filter.inl> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.inl> | |||
#include <cpphibernate/driver/mariadb/context/create_update_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
static std::string build_create_update_query( | |||
const table_t& table, | |||
const filter_t * filter, | |||
const field_t * owner); | |||
static std::string execute_create_update( | |||
const table_t& table, | |||
const create_update_context& context, | |||
::cppmariadb::statement * statement); | |||
/* table_t */ | |||
std::string table_t::create_update(const create_update_context& context) const | |||
{ return create_update_exec(context); } | |||
std::string table_t::create_update_exec(const create_update_context& context) const | |||
{ | |||
auto * statement = context.is_update() | |||
? get_statement_update(*context.filter, context.owner_field) | |||
: get_statement_insert_into(); | |||
return execute_create_update(*this, context, statement); | |||
} | |||
::cppmariadb::statement* table_t::get_statement_insert_into() const | |||
{ | |||
if (!_statement_insert_into) | |||
{ | |||
auto query = build_create_update_query(*this, nullptr, nullptr); | |||
_statement_insert_into.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return _statement_insert_into->empty() | |||
? nullptr | |||
: _statement_insert_into.get(); | |||
} | |||
::cppmariadb::statement* table_t::get_statement_update(const filter_t& filter, const field_t * owner) const | |||
{ | |||
auto key = std::make_tuple(filter.cache_id, owner); | |||
auto it = _statement_update.find(key); | |||
if (it == _statement_update.end()) | |||
{ | |||
auto query = build_create_update_query(*this, &filter, owner); | |||
it = _statement_update.emplace(key, ::cppmariadb::statement(query)).first; | |||
} | |||
return it->second.empty() | |||
? nullptr | |||
: &it->second; | |||
} | |||
std::string build_create_update_query(const table_t& table, const filter_t* filter, const field_t* owner) | |||
{ | |||
std::ostringstream os; | |||
size_t index = 0; | |||
bool is_update = static_cast<bool>(filter); | |||
bool is_create = !is_update; | |||
/* INSER INTO / UPDATE */ | |||
os << (is_update | |||
? "UPDATE" | |||
: "INSERT INTO") | |||
<< " `" | |||
<< table.name | |||
<< "`"; | |||
/* primary key */ | |||
if (is_create) | |||
{ | |||
assert(table.primary_key_field); | |||
auto& key_info = *table.primary_key_field; | |||
if (!key_info.value_is_auto_incremented) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
os << "`" | |||
<< key_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?" | |||
<< key_info.name | |||
<< "?" | |||
<< key_info.convert_to_close; | |||
} | |||
} | |||
/* base table key fields */ | |||
if ( static_cast<bool>(table.base_table) | |||
&& ( is_create | |||
|| !filter->is_excluded(*table.base_table))) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
auto& base_table_info = *table.base_table; | |||
assert(base_table_info.primary_key_field); | |||
auto& key_info = *base_table_info.primary_key_field; | |||
os << "`" | |||
<< key_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?" | |||
<< key_info.name | |||
<< "?" | |||
<< key_info.convert_to_close; | |||
} | |||
/* foreign table one fields */ | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
if (is_update && filter->is_excluded(field_info)) | |||
continue; | |||
assert(field_info.referenced_table); | |||
assert(field_info.referenced_table->primary_key_field); | |||
if (field_info.referenced_table->is_used_in_container) | |||
continue; | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
auto& key_info = *field_info.referenced_table->primary_key_field; | |||
os << "`" | |||
<< key_info.table.name | |||
<< "_id_" | |||
<< field_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?" | |||
<< key_info.table.name | |||
<< "_id_" | |||
<< field_info.name | |||
<< "?" | |||
<< key_info.convert_to_close; | |||
} | |||
/* foreign fields */ | |||
for (auto& ptr : table.foreign_key_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
if (is_update && ptr != owner) | |||
continue; | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
auto& field_info = *ptr; | |||
assert(field_info.table.primary_key_field); | |||
auto& key_info = *field_info.table.primary_key_field; | |||
os << "`" | |||
<< field_info.table.name | |||
<< "_id_" | |||
<< field_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?" | |||
<< field_info.table.name | |||
<< "_id_" | |||
<< field_info.name | |||
<< "?" | |||
<< key_info.convert_to_close; | |||
if (field_info.value_is_ordered) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
os << "`" | |||
<< field_info.table.name | |||
<< "_index_" | |||
<< field_info.name | |||
<< "`=?\?"; | |||
} | |||
} | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(ptr); | |||
auto& field_info = *ptr; | |||
if (is_update && filter->is_excluded(field_info)) | |||
continue; | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
os << "`" | |||
<< field_info.name | |||
<< "`=" | |||
<< field_info.convert_to_open | |||
<< "?" | |||
<< field_info.name | |||
<< "?" | |||
<< field_info.convert_to_close; | |||
} | |||
/* type field for derived tables */ | |||
if ( !table.derived_tables.empty() | |||
&& !table.base_table | |||
&& is_create) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
else | |||
os << " SET "; | |||
os << "`__type`=?__type?"; | |||
} | |||
/* where primary key (for update) */ | |||
if (is_update) | |||
{ | |||
assert(table.primary_key_field); | |||
auto& key_info = *table.primary_key_field; | |||
os << " WHERE `" | |||
<< key_info.name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?" | |||
<< key_info.name | |||
<< "?" | |||
<< key_info.convert_to_close; | |||
} | |||
return index == 0 && !(table.primary_key_field->value_is_auto_incremented && is_create) | |||
? std::string() | |||
: os.str(); | |||
} | |||
std::string execute_create_update( | |||
const table_t& table, | |||
const create_update_context& context, | |||
::cppmariadb::statement * statement) | |||
{ | |||
auto& connection = context.connection; | |||
auto* filter = context.filter; | |||
size_t index = 0; | |||
bool is_update = context.is_update(); | |||
bool is_create = context.is_create(); | |||
std::string primary_key; | |||
if (statement) statement->clear(); | |||
/* primary key */ | |||
assert(table.primary_key_field); | |||
if (is_update) | |||
{ | |||
primary_key = table.get_primary_key(context); | |||
} | |||
else if (!table.primary_key_field->value_is_auto_incremented) | |||
{ | |||
primary_key = table.primary_key_field->generate_value(context.connection); | |||
if (statement) statement->set(index, primary_key); | |||
++index; | |||
} | |||
/* base_key */ | |||
if ( table.base_table | |||
&& ( is_create | |||
|| !filter->is_excluded(*table.base_table))) | |||
{ | |||
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; | |||
} | |||
if (is_update && filter->is_excluded(table)) | |||
return primary_key; | |||
/* foreign table one fields */ | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
if (is_update && filter->is_excluded(field)) | |||
continue; | |||
if (field.referenced_table->is_used_in_container) | |||
continue; | |||
/* insert/update dataset */ | |||
value_t key = field.foreign_create_update(context); | |||
if (key.has_value()) | |||
{ | |||
if (statement) statement->set(index, *key); | |||
} | |||
else if (field.value_is_nullable) | |||
{ | |||
if (statement) statement->set_null(index); | |||
} | |||
else | |||
{ | |||
throw exception("Received null key for non nullable foreign dataset!"); | |||
} | |||
++index; | |||
/* 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); | |||
*/ | |||
} | |||
} | |||
/* foreign key fields */ | |||
for (auto& ptr : table.foreign_key_fields) | |||
{ | |||
assert(ptr); | |||
if (is_update && ptr != context.owner_field) | |||
continue; | |||
auto& field_info = *ptr; | |||
bool set_value = | |||
context.owner_field | |||
&& ptr == context.owner_field; | |||
if (set_value) | |||
{ | |||
assert(!context.owner_key.empty()); | |||
if (statement) statement->set(index, context.owner_key); | |||
} | |||
else | |||
{ | |||
if (statement) statement->set_null(index); | |||
} | |||
++index; | |||
if (field_info.value_is_ordered) | |||
{ | |||
if (set_value) | |||
{ | |||
if (statement) statement->set(index, context.index); | |||
} | |||
else | |||
{ | |||
if (statement) statement->set(index, 0); | |||
} | |||
++index; | |||
} | |||
} | |||
/* data fields */ | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(ptr); | |||
if (is_update && filter->is_excluded(*ptr)) | |||
continue; | |||
auto& field_info = *ptr; | |||
auto value = field_info.get(context); | |||
if (value.has_value()) | |||
{ | |||
if (statement) statement->set(index, *value); | |||
} | |||
else | |||
{ | |||
if (statement) statement->set_null(index); | |||
} | |||
++index; | |||
} | |||
/* type field for derived tables */ | |||
if ( !table.derived_tables.empty() | |||
&& !table.base_table | |||
&& is_create) | |||
{ | |||
if (statement) statement->set(index, context.derived_table | |||
? context.derived_table->id | |||
: table.id); | |||
++index; | |||
} | |||
/* where primary key (for update) */ | |||
if (is_update) | |||
{ | |||
assert(table.primary_key_field); | |||
if (statement) statement->set(index, *table.primary_key_field->get(context)); | |||
++index; | |||
} | |||
/* execute */ | |||
if (statement) | |||
{ | |||
if (is_create) | |||
{ | |||
cpphibernate_log_debug("execute INSERT query: " << std::endl << statement->query(connection) << std::endl); | |||
} | |||
else | |||
{ | |||
cpphibernate_log_debug("execute UPDATE query: " << std::endl << statement->query(connection) << std::endl); | |||
} | |||
if ( table.primary_key_field->value_is_auto_incremented | |||
&& is_create) | |||
{ | |||
auto id = connection.execute_id(*statement); | |||
primary_key = cppcore::to_string(id); | |||
} | |||
else | |||
{ | |||
auto count = connection.execute_rows(*statement); | |||
if (count > 1) | |||
throw exception("Expected one/ row to be inserted/updated!"); | |||
cpphibernate_log_debug(count << " rows inserted/updated" << std::endl); | |||
} | |||
table.primary_key_field->set(context, primary_key); | |||
} | |||
/* foreign table many fields */ | |||
for (auto& ptr : table.foreign_table_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
auto& ref_table = *field.referenced_table; | |||
if (!ref_table.is_used_in_container) | |||
continue; | |||
if ( is_update | |||
&& ( filter->is_excluded(field) | |||
|| filter->is_excluded(ref_table))) | |||
continue; | |||
/* 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); | |||
*/ | |||
} | |||
/* update elements */ | |||
auto next_context = context; | |||
next_context.owner_field = ptr; | |||
next_context.owner_key = primary_key; | |||
next_context.derived_table = nullptr; | |||
field.foreign_create_update(next_context); | |||
/* delete non referenced elements */ | |||
if (context.is_update()) | |||
{ | |||
/* TODO | |||
table_set processed; | |||
ref_table.destroy_cleanup(context, processed, true, true); | |||
*/ | |||
} | |||
} | |||
return primary_key; | |||
} |
@@ -1,10 +1,16 @@ | |||
#include <cppcore/misc/indent.h> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
#include <cpphibernate/driver/mariadb/classes/schema/schema.h> | |||
#include <cpphibernate/types.inl> | |||
#include <cpphibernate/driver/mariadb/classes/tables/table.inl> | |||
#include <cpphibernate/driver/mariadb/classes/schema/schema.inl> | |||
#include <cpphibernate/driver/mariadb/context/init_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
static std::string build_init_stage1_query(const table_t& table); | |||
static std::string build_init_stage2_query(const table_t& table); | |||
void table_t::init(const init_context& context, init_stage stage) const | |||
{ | |||
switch (stage) | |||
@@ -13,7 +19,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
{ | |||
auto& statement = get_statement_init_stage1(); | |||
auto& connection = context.connection; | |||
cpphibernate_log_debug("execute CREATE TABLE query: " << statement.query(connection)); | |||
cpphibernate_log_debug("execute CREATE TABLE query: " << std::endl << statement.query(connection) << std::endl); | |||
connection.execute(statement); | |||
} | |||
break; | |||
@@ -24,7 +30,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
auto& connection = context.connection; | |||
if (!statement) | |||
return; | |||
cpphibernate_log_debug("execute ALTER TABLE query: " << statement->query(connection)); | |||
cpphibernate_log_debug("execute ALTER TABLE query: " << std::endl << statement->query(connection) << std::endl); | |||
connection.execute(*statement); | |||
} | |||
break; | |||
@@ -37,16 +43,30 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
::cppmariadb::statement& table_t::get_statement_init_stage1() const | |||
{ | |||
using namespace ::cppcore; | |||
if (!_statement_init_stage1) | |||
_statement_init_stage1.reset(new ::cppmariadb::statement(build_init_stage1_query(*this))); | |||
return *_statement_init_stage1; | |||
} | |||
if (_statement_init_stage1) | |||
return *_statement_init_stage1; | |||
::cppmariadb::statement* table_t::get_statement_init_stage2() const | |||
{ | |||
if (!_statement_init_stage2) | |||
_statement_init_stage2.reset(new ::cppmariadb::statement(build_init_stage2_query(*this))); | |||
return _statement_init_stage2->empty() | |||
? nullptr | |||
: _statement_init_stage2.get(); | |||
} | |||
std::string build_init_stage1_query(const table_t& table) | |||
{ | |||
using namespace ::cppcore; | |||
std::ostringstream os; | |||
/* CREATE TABLE */ | |||
os << "CREATE TABLE IF NOT EXISTS `" | |||
<< name | |||
<< table.name | |||
<< "`" | |||
<< indent | |||
<< "(" | |||
@@ -54,8 +74,8 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
/* primary key */ | |||
{ | |||
assert(primary_key_field); | |||
auto& key_info = *primary_key_field; | |||
assert(table.primary_key_field); | |||
auto& key_info = *table.primary_key_field; | |||
auto args = key_info.create_arguments; | |||
os << indent | |||
@@ -70,9 +90,9 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* base table key fields */ | |||
if (static_cast<bool>(base_table)) | |||
if (static_cast<bool>(table.base_table)) | |||
{ | |||
auto& base_table_info = *base_table; | |||
auto& base_table_info = *table.base_table; | |||
assert(base_table_info.primary_key_field); | |||
auto& key_info = *base_table_info.primary_key_field; | |||
@@ -86,7 +106,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* foreign table one fields */ | |||
for (auto& ptr : foreign_table_one_fields) | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -112,7 +132,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* foreign fields */ | |||
for (auto& ptr : foreign_key_fields) | |||
for (auto& ptr : table.foreign_key_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -140,7 +160,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* data fields */ | |||
for (auto& ptr : data_fields) | |||
for (auto& ptr : table.data_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -156,8 +176,8 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* type field for derived tables */ | |||
if (!derived_tables.empty() && | |||
!base_table) | |||
if (!table.derived_tables.empty() && | |||
!table.base_table) | |||
{ | |||
os << indent | |||
<< "`__type` INT UNSIGNED NOT NULL,"; | |||
@@ -165,8 +185,8 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
/* PRIMARY KEY */ | |||
{ | |||
assert(primary_key_field); | |||
auto& key_info = *primary_key_field; | |||
assert(table.primary_key_field); | |||
auto& key_info = *table.primary_key_field; | |||
os << indent | |||
<< "PRIMARY KEY ( `" | |||
@@ -176,8 +196,8 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
/* UNIQUE INDEX primary key */ | |||
{ | |||
assert(primary_key_field); | |||
auto& key_info = *primary_key_field; | |||
assert(table.primary_key_field); | |||
auto& key_info = *table.primary_key_field; | |||
os << ',' | |||
<< indent | |||
@@ -189,9 +209,9 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* UNIQUE INDEX base table keys */ | |||
if (base_table) | |||
if (table.base_table) | |||
{ | |||
auto& table_info = *base_table; | |||
auto& table_info = *table.base_table; | |||
assert(table_info.primary_key_field); | |||
auto& key_info = *table_info.primary_key_field; | |||
@@ -206,7 +226,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* INDEX foreign table one fields */ | |||
for (auto& ptr : foreign_table_one_fields) | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -233,7 +253,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* INDEX foreign fields */ | |||
for (auto& ptr : foreign_key_fields) | |||
for (auto& ptr : table.foreign_key_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -260,39 +280,33 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
<< indent | |||
<< "DEFAULT CHARACTER SET = utf8"; | |||
_statement_init_stage1.reset(new ::cppmariadb::statement(os.str())); | |||
return *_statement_init_stage1; | |||
return os.str(); | |||
} | |||
::cppmariadb::statement* table_t::get_statement_init_stage2() const | |||
std::string build_init_stage2_query(const table_t& table) | |||
{ | |||
using namespace ::cppcore; | |||
if (_statement_init_stage2) | |||
return _statement_init_stage2->empty() | |||
? nullptr | |||
: _statement_init_stage2.get(); | |||
std::ostringstream os; | |||
/* ALTER TABLE */ | |||
os << "ALTER TABLE `" | |||
<< name | |||
<< table.name | |||
<< "`" | |||
<< incindent; | |||
size_t index = 0; | |||
/* CONSTRAINT base table */ | |||
if (base_table) | |||
if (table.base_table) | |||
{ | |||
assert(base_table->primary_key_field); | |||
auto& ref_key_info = *base_table->primary_key_field; | |||
assert(table.base_table->primary_key_field); | |||
auto& ref_key_info = *table.base_table->primary_key_field; | |||
if (index++) os << ","; | |||
os << indent | |||
<< "ADD CONSTRAINT `fk_" | |||
<< name | |||
<< table.name | |||
<< "_" | |||
<< ref_key_info.name | |||
<< "`" | |||
@@ -317,7 +331,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* CONSTRAINT foreign table one fields */ | |||
for (auto& ptr : foreign_table_one_fields) | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -333,7 +347,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
if (index++) os << ","; | |||
os << indent | |||
<< "ADD CONSTRAINT `fk_" | |||
<< name | |||
<< table.name | |||
<< "_" | |||
<< ref_key_info.table.name | |||
<< "_" | |||
@@ -365,7 +379,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
} | |||
/* CONSTRAINT foreign fields */ | |||
for (auto& ptr : foreign_key_fields) | |||
for (auto& ptr : table.foreign_key_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
@@ -376,7 +390,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
if (index++) os << ","; | |||
os << indent | |||
<< "ADD CONSTRAINT `fk_" | |||
<< name | |||
<< table.name | |||
<< "_" | |||
<< field_info.table.name | |||
<< "_" | |||
@@ -404,10 +418,7 @@ void table_t::init(const init_context& context, init_stage stage) const | |||
<< decindent; | |||
} | |||
_statement_init_stage2.reset(new ::cppmariadb::statement(index == 0 | |||
return index == 0 | |||
? std::string { } | |||
: os.str())); | |||
return _statement_init_stage2->empty() | |||
? nullptr | |||
: _statement_init_stage2.get(); | |||
: os.str(); | |||
} |
@@ -3,6 +3,9 @@ | |||
#include <cppcore/misc/indent.h> | |||
#include <cpphibernate/types.inl> | |||
#include <cpphibernate/driver/mariadb/context/data_context.inl> | |||
using namespace ::cpphibernate; | |||
using namespace ::cpphibernate::mariadb; | |||
@@ -18,13 +21,9 @@ std::ostream& table_t::print(std::ostream& os) const | |||
os << indent << '{' | |||
<< incindent | |||
<< indent << "\"id\": " << id << "," | |||
// TODO | |||
<< indent << "\"dataset_id\": " << dataset_id << "," | |||
<< indent << "\"base_dataset_id\": " << base_dataset_id << "," | |||
<< indent << "\"derived_dataset_ids\": " << make_print_container(derived_dataset_ids, false) << "," | |||
// TODO | |||
// << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
// << indent << "\"table_name\": \"" << table_name << "\"," | |||
<< indent << "\"name\": \"" << name << "\"," | |||
<< indent << "\"base_table\": " << (base_table ? std::string("\"") + base_table->name + "\"" : "null") << "," | |||
<< indent << "\"derived_tables\":" << make_print_container(derived_tables, true, [](auto& s, auto& ptr){ | |||
@@ -55,12 +54,60 @@ std::ostream& table_t::print(std::ostream& os) const | |||
return os; | |||
} | |||
void table_t::init() | |||
std::string table_t::get_primary_key(const data_context& context) const | |||
{ | |||
assert(primary_key_field); | |||
if ( primary_key_field->is_default(context) | |||
&& base_table) | |||
{ | |||
auto key = get_key_from_base(context); | |||
primary_key_field->set(context, key); | |||
return key; | |||
} | |||
else | |||
{ | |||
return *primary_key_field->get(context); | |||
} | |||
} | |||
std::string table_t::get_key_from_base(const data_context& context) const | |||
{ | |||
if (!base_table) | |||
{ | |||
throw exception(std::string("table has no base table: ") + name); | |||
} | |||
auto& statement = get_statement_key_from_base(); | |||
auto base_key = base_table->get_primary_key(context); | |||
statement.set(0, base_key); | |||
auto result = context.connection.execute_stored(statement); | |||
if (!result) | |||
throw exception("unable to fetch key from database: unable to execute query!"); | |||
auto row = result->next(); | |||
if (!row) | |||
throw exception("unable to fetch key from database: result set is empty!"); | |||
return row->at(0).get<std::string>(); | |||
} | |||
::cppmariadb::statement& table_t::get_statement_key_from_base() const | |||
{ | |||
/* build field lookup */ | |||
for (auto& f : fields) | |||
if (!_statement_key_from_base) | |||
{ | |||
assert(static_cast<bool>(f)); | |||
_lookup.emplace(f->id, f.get()); | |||
if (!base_table) | |||
throw exception(std::string("table has no base table: ") + name); | |||
assert(primary_key_field); | |||
assert(base_table); | |||
assert(base_table->primary_key_field); | |||
auto& key_info = *primary_key_field; | |||
auto& base_key = *base_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "SELECT `" | |||
<< key_info.name | |||
<< "` FROM `" | |||
<< key_info.table.name | |||
<< "` WHERE `" | |||
<< base_key.table.name | |||
<< "_id`=?\?"; | |||
_statement_key_from_base.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *_statement_key_from_base; | |||
} |
@@ -45,10 +45,10 @@ TEST(CppHibernateTests, create_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.create(t1); | |||
} | |||
#if 0 | |||
TEST(CppHibernateTests, create_test2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
@@ -576,4 +576,5 @@ TEST(CppHibernateTests, create_double_usage) | |||
EXPECT_EQ(d.single_item->id, 1001); | |||
EXPECT_EQ(d.multiple_items[0].id, 1002); | |||
EXPECT_EQ(d.multiple_items[1].id, 1003); | |||
} | |||
} | |||
#endif |