diff --git a/include/cpphibernate.h b/include/cpphibernate.h index e895d5b..b7475c7 100644 --- a/include/cpphibernate.h +++ b/include/cpphibernate.h @@ -5,3 +5,5 @@ #include "cpphibernate/modifier.h" #include "cpphibernate/schema.h" #include "cpphibernate/types.h" + +#include "cpphibernate/types.inl" diff --git a/include/cpphibernate/context.h b/include/cpphibernate/context.h index e3e2b6c..df99aa4 100644 --- a/include/cpphibernate/context.h +++ b/include/cpphibernate/context.h @@ -1,3 +1,5 @@ #pragma once #include "context/context.h" + +#include "context/context.inl" diff --git a/include/cpphibernate/context/context.h b/include/cpphibernate/context/context.h index ef2c7e9..acf00a5 100644 --- a/include/cpphibernate/context/context.h +++ b/include/cpphibernate/context/context.h @@ -102,5 +102,3 @@ namespace cpphibernate constexpr decltype(auto) make_context_ptr(T_schema&& schema, T_args&&... args); } - -#include "context.inl" diff --git a/include/cpphibernate/context/context.inl b/include/cpphibernate/context/context.inl index d6bd437..b8815e1 100644 --- a/include/cpphibernate/context/context.inl +++ b/include/cpphibernate/context/context.inl @@ -21,7 +21,9 @@ namespace cpphibernate template struct init_builder< mp::list, - mp::enable_if_t>>> + mp::enable_if_t< + mp::is_valid_v().init(std::declval()))> + && mp::is_same_v>>> { 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 { }; -#if 0 template - struct create_impl, void> + struct create_builder< + mp::list, + mp::enable_if_t< + mp::is_valid_v().create(std::declval()))>>> { static constexpr decltype(auto) apply(T_impl& impl, T_dataset& dataset) { return impl.create(dataset); } }; -#endif + + constexpr decltype(auto) create = mp::generic_predicate { }; + /* read_builder */ template diff --git a/include/cpphibernate/driver/mariadb.h b/include/cpphibernate/driver/mariadb.h index 21a9ac5..2f7ab51 100644 --- a/include/cpphibernate/driver/mariadb.h +++ b/include/cpphibernate/driver/mariadb.h @@ -3,7 +3,12 @@ #include #ifdef CPPHIBERNATE_HAS_CPPMARIADB - #include + #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 { diff --git a/include/cpphibernate/driver/mariadb/classes/attributes.h b/include/cpphibernate/driver/mariadb/classes/attributes.h index 997d436..56b5a6f 100644 --- a/include/cpphibernate/driver/mariadb/classes/attributes.h +++ b/include/cpphibernate/driver/mariadb/classes/attributes.h @@ -1,3 +1,5 @@ #pragma once #include "attributes/attributes.h" + +#include "attributes/attributes.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h index 20e2341..5cef869 100644 --- a/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h +++ b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.h @@ -1,8 +1,8 @@ #pragma once -#include +#include -#include "../forward.h" +#include namespace cpphibernate { namespace mariadb { @@ -31,7 +31,12 @@ namespace mariadb { /** * @brief Set of attributes. */ - struct attributes_t; + struct attributes_t : + public std::set + { + using base_type = std::set; + 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" diff --git a/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl index ff7179c..896005a 100644 --- a/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl +++ b/include/cpphibernate/driver/mariadb/classes/attributes/attributes.inl @@ -1,12 +1,10 @@ #pragma once -#include +#include "attributes.h" #include #include -#include "attributes.h" - namespace cpphibernate { namespace mariadb { @@ -62,13 +60,4 @@ namespace mariadb { } - /* attributes_t */ - - struct attributes_t : - public std::set - { - using base_type = std::set; - using base_type::base_type; - }; - } } diff --git a/include/cpphibernate/driver/mariadb/classes/fields.h b/include/cpphibernate/driver/mariadb/classes/fields.h index 69c000a..79b9a80 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields.h +++ b/include/cpphibernate/driver/mariadb/classes/fields.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field.h b/include/cpphibernate/driver/mariadb/classes/fields/field.h index 0205dc8..af5b156 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field.h @@ -1,14 +1,17 @@ #pragma once -#include -#include +#include -#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; + namespace __impl { @@ -102,5 +154,3 @@ namespace mariadb { constexpr decltype(auto) make_field = mp::generic_predicate<__impl::field_builder> { }; } } - -#include "field.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field.inl b/include/cpphibernate/driver/mariadb/classes/fields/field.inl index 264b173..d40fe22 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field.inl +++ b/include/cpphibernate/driver/mariadb/classes/fields/field.inl @@ -1,13 +1,9 @@ #pragma once -#include -#include -#include - #include "field.h" -#include "field_data.h" -#include "field_primary_key.h" -#include "field_foreign_table.h" + +#include +#include namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_data.h b/include/cpphibernate/driver/mariadb/classes/fields/field_data.h index 540fa22..3a5b297 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_data.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_data.h @@ -35,5 +35,3 @@ namespace mariadb { }; } } - -#include "field_data.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h index e73ebbc..2a6c011 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_foreign_table.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h index fc5d364..61f6369 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl index c1e9377..6b348c0 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_primary_key.inl +++ b/include/cpphibernate/driver/mariadb/classes/fields/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 + ::is_default(const data_context& context) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + return key_props.is_default(this->_field.getter(dataset)); + } + + template< + typename T_field> + std::string field_primary_key_t + ::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(); + } + } } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h index 58ca545..ec6a872 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl index fa54378..e793c83 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_simple.inl @@ -22,6 +22,7 @@ namespace mariadb { p_schema, p_table, p_field) + , _field(p_field) { } } } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_value.h b/include/cpphibernate/driver/mariadb/classes/fields/field_value.h index ed11729..cb23354 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_value.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_value.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl b/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl index f958c40..1243919 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl +++ b/include/cpphibernate/driver/mariadb/classes/fields/field_value.inl @@ -34,4 +34,22 @@ namespace mariadb { this->convert_from_close = value_props.convert_from_close() + this->convert_from_close; } + template + value_t field_value_t:: + get(const data_context& context) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + return value_props.convert_from(this->_field.getter(dataset)); + } + + template + void field_value_t + ::set(const data_context& context, const value_t& value) const + { + using dataset_type = typename mp::decay_t::dataset_type; + auto& dataset = context.get(); + this->_field.setter(dataset, value_props.convert_to(value)); + } + } } diff --git a/include/cpphibernate/driver/mariadb/classes/fields/fields.h b/include/cpphibernate/driver/mariadb/classes/fields/fields.h index 1d86301..da1bd0a 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/fields.h +++ b/include/cpphibernate/driver/mariadb/classes/fields/fields.h @@ -1,9 +1,8 @@ #pragma once -#include -#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" diff --git a/include/cpphibernate/driver/mariadb/classes/fields/fields.inl b/include/cpphibernate/driver/mariadb/classes/fields/fields.inl index f33b772..fd7d8bc 100644 --- a/include/cpphibernate/driver/mariadb/classes/fields/fields.inl +++ b/include/cpphibernate/driver/mariadb/classes/fields/fields.inl @@ -1,10 +1,6 @@ #pragma once -#include -#include - #include "field.h" -#include "fields.h" namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/classes/forward.h b/include/cpphibernate/driver/mariadb/classes/forward.h deleted file mode 100644 index 8b21490..0000000 --- a/include/cpphibernate/driver/mariadb/classes/forward.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include - -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; - using table_ptr_u = std::unique_ptr; - using schema_ptr_u = std::unique_ptr; - -} } diff --git a/include/cpphibernate/driver/mariadb/classes/schema.h b/include/cpphibernate/driver/mariadb/classes/schema.h index b5e715e..ab9f90e 100644 --- a/include/cpphibernate/driver/mariadb/classes/schema.h +++ b/include/cpphibernate/driver/mariadb/classes/schema.h @@ -1,3 +1,5 @@ #pragma once #include "schema/schema.h" + +#include "schema/schema.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/schema/schema.h b/include/cpphibernate/driver/mariadb/classes/schema/schema.h index 4c5aa6f..7a7ed6c 100644 --- a/include/cpphibernate/driver/mariadb/classes/schema/schema.h +++ b/include/cpphibernate/driver/mariadb/classes/schema/schema.h @@ -1,10 +1,9 @@ #pragma once -#include +#include -#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; + using field_map = std::map; 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; + namespace __impl { @@ -82,5 +95,3 @@ namespace mariadb { constexpr decltype(auto) make_schema = mp::generic_predicate<__impl::schema_builder> { }; } } - -#include "schema.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/schema/schema.inl b/include/cpphibernate/driver/mariadb/classes/schema/schema.inl index e6c016f..31ff90b 100644 --- a/include/cpphibernate/driver/mariadb/classes/schema/schema.inl +++ b/include/cpphibernate/driver/mariadb/classes/schema/schema.inl @@ -1,9 +1,10 @@ #pragma once -#include - #include "schema.h" +#include +#include "../tables/tables.inl" + namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/classes/tables.h b/include/cpphibernate/driver/mariadb/classes/tables.h index 411f73b..6f70f09 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables.h +++ b/include/cpphibernate/driver/mariadb/classes/tables.h @@ -1,4 +1 @@ #pragma once - -#include "tables/table.h" -#include "tables/tables.h" diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table.h b/include/cpphibernate/driver/mariadb/classes/tables/table.h index 06763af..8ed96bb 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/table.h +++ b/include/cpphibernate/driver/mariadb/classes/tables/table.h @@ -1,16 +1,27 @@ #pragma once +#include #include -#include -#include +#include -#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; //!< vector of size_t using table_vector = std::vector; //!< vector of constant field pointers using field_vector = std::vector; //!< vector of constant field pointers - using field_map = std::map; //!< 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; + using statement_map = std::map; + + 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; + namespace __impl { @@ -139,5 +209,3 @@ namespace mariadb { constexpr decltype(auto) make_table = mp::generic_predicate<__impl::table_builder> { }; } } - -#include "table.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table.inl b/include/cpphibernate/driver/mariadb/classes/tables/table.inl index bf0b37d..56db483 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/table.inl +++ b/include/cpphibernate/driver/mariadb/classes/tables/table.inl @@ -1,11 +1,11 @@ #pragma once -#include -#include +#include #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 { diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h index 39a2b90..65a2d3b 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_polymorphic.h @@ -1,8 +1,8 @@ #pragma once -#include -#include "table.h" + + namespace cpphibernate { namespace mariadb { @@ -35,5 +35,3 @@ namespace mariadb { }; } } - -#include "table_polymorphic.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h index abd138a..945ed34 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h +++ b/include/cpphibernate/driver/mariadb/classes/tables/table_simple.h @@ -1,8 +1,8 @@ #pragma once -#include -#include "table.h" + + namespace cpphibernate { namespace mariadb { @@ -35,5 +35,3 @@ namespace mariadb { }; } } - -#include "table_simple.inl" diff --git a/include/cpphibernate/driver/mariadb/classes/tables/tables.h b/include/cpphibernate/driver/mariadb/classes/tables/tables.h index 066b658..93c3907 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/tables.h +++ b/include/cpphibernate/driver/mariadb/classes/tables/tables.h @@ -1,9 +1,5 @@ #pragma once -#include - -#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" diff --git a/include/cpphibernate/driver/mariadb/classes/tables/tables.inl b/include/cpphibernate/driver/mariadb/classes/tables/tables.inl index d49bf94..7be19bc 100644 --- a/include/cpphibernate/driver/mariadb/classes/tables/tables.inl +++ b/include/cpphibernate/driver/mariadb/classes/tables/tables.inl @@ -1,10 +1,9 @@ #pragma once -#include -#include - #include "tables.h" +#include "table.inl" + namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/context.h b/include/cpphibernate/driver/mariadb/context.h index 6816c04..f67177c 100644 --- a/include/cpphibernate/driver/mariadb/context.h +++ b/include/cpphibernate/driver/mariadb/context.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/context/base_context.h b/include/cpphibernate/driver/mariadb/context/base_context.h index c19b759..48687ef 100644 --- a/include/cpphibernate/driver/mariadb/context/base_context.h +++ b/include/cpphibernate/driver/mariadb/context/base_context.h @@ -1,7 +1,6 @@ #pragma once #include -#include namespace cpphibernate { namespace mariadb { @@ -25,5 +24,3 @@ namespace mariadb { }; } } - -#include "base_context.inl" diff --git a/include/cpphibernate/driver/mariadb/context/create_update_context.h b/include/cpphibernate/driver/mariadb/context/create_update_context.h new file mode 100644 index 0000000..9ff8b68 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/create_update_context.h @@ -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 + 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 + 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; + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/create_update_context.inl b/include/cpphibernate/driver/mariadb/context/create_update_context.inl new file mode 100644 index 0000000..b4cd1d7 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/create_update_context.inl @@ -0,0 +1,57 @@ +#pragma once + +#include "create_update_context.h" + +namespace cpphibernate { +namespace mariadb { + + /* create_update_context */ + + template + 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 + 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(filter); } + + bool create_update_context::is_update() const + { return static_cast(filter); } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/data_context.h b/include/cpphibernate/driver/mariadb/context/data_context.h new file mode 100644 index 0000000..2b5d549 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/data_context.h @@ -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 + 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 + 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 + 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 + inline decltype(auto) get() const; + + private: + /** + * @brief Set the dataset that is stored in this context. + */ + template + inline void set(T_dataset& dataset); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/context/data_context.inl b/include/cpphibernate/driver/mariadb/context/data_context.inl new file mode 100644 index 0000000..170a38b --- /dev/null +++ b/include/cpphibernate/driver/mariadb/context/data_context.inl @@ -0,0 +1,99 @@ +#pragma once + +#include "data_context.h" + +#include "../classes/schema/schema.inl" + +namespace cpphibernate { +namespace mariadb { + + /* data_context */ + + template + 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 + decltype(auto) data_context::get() const + { + using dataset_type = mp::decay_t; + + /* 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); + + /* 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::name() + + " is not a derived type of dataset with id " + std::to_string(_dataset_id)); + } + } + + /* return the dataset */ + return *static_cast(_dataset); + } + + template + void data_context::set(T_dataset& dataset) + { + _dataset_id = get_type_id(hana::type_c>); + _dataset = &dataset; + _table = nullptr; + } + + namespace __impl + { + + /* change_context_builder */ + + template + struct change_context_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for change_context(...)!"); } + }; + + template + struct change_context_builder< + mp::list, + mp::enable_if_t< + mp::is_base_of_v>>> + { + static constexpr decltype(auto) apply(const T_context& context, T_dataset& dataset) + { + auto new_context = context; + new_context.set(dataset); + return new_context; + } + }; + + } + +} } diff --git a/include/cpphibernate/driver/mariadb/context/init_context.h b/include/cpphibernate/driver/mariadb/context/init_context.h index 110132d..03ce5cd 100644 --- a/include/cpphibernate/driver/mariadb/context/init_context.h +++ b/include/cpphibernate/driver/mariadb/context/init_context.h @@ -1,7 +1,5 @@ #pragma once -#include -#include #include "base_context.h" namespace cpphibernate { @@ -22,5 +20,3 @@ namespace mariadb { }; } } - -#include "init_context.inl" diff --git a/include/cpphibernate/driver/mariadb/context/init_context.inl b/include/cpphibernate/driver/mariadb/context/init_context.inl index 7e01441..f9815ef 100644 --- a/include/cpphibernate/driver/mariadb/context/init_context.inl +++ b/include/cpphibernate/driver/mariadb/context/init_context.inl @@ -2,6 +2,8 @@ #include "init_context.h" +#include "base_context.inl" + namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/driver.h b/include/cpphibernate/driver/mariadb/driver.h index 7f3cd41..d46e8fd 100644 --- a/include/cpphibernate/driver/mariadb/driver.h +++ b/include/cpphibernate/driver/mariadb/driver.h @@ -1,8 +1,5 @@ #pragma once -#include - -#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 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 + 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" diff --git a/include/cpphibernate/driver/mariadb/driver.inl b/include/cpphibernate/driver/mariadb/driver.inl index f91df06..211b948 100644 --- a/include/cpphibernate/driver/mariadb/driver.inl +++ b/include/cpphibernate/driver/mariadb/driver.inl @@ -1,14 +1,21 @@ #pragma once -#include "driver.h" -#include "impl/driver_impl.inl" + + namespace cpphibernate { namespace mariadb { template driver_t::driver_t(T_schema&& p_schema) - : _impl(*this, std::forward(p_schema)) + : _impl (*this, std::forward(p_schema)) + , _connection (nullptr) + { } + + template + driver_t::driver_t(T_schema&& p_schema, ::cppmariadb::connection& p_connection) + : _impl (*this, std::forward(p_schema)) + , _connection (&p_connection) { } std::ostream& driver_t::print(std::ostream& os) const diff --git a/include/cpphibernate/driver/mariadb/helper.h b/include/cpphibernate/driver/mariadb/helper.h index b12e504..51c1c5a 100644 --- a/include/cpphibernate/driver/mariadb/helper.h +++ b/include/cpphibernate/driver/mariadb/helper.h @@ -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" diff --git a/include/cpphibernate/driver/mariadb/helper/key_properties.h b/include/cpphibernate/driver/mariadb/helper/key_properties.h index 4238978..05f37a0 100644 --- a/include/cpphibernate/driver/mariadb/helper/key_properties.h +++ b/include/cpphibernate/driver/mariadb/helper/key_properties.h @@ -1,6 +1,6 @@ #pragma once -#include + namespace cpphibernate { namespace mariadb { @@ -37,5 +37,3 @@ namespace mariadb { }; } } - -#include "key_properties.inl" diff --git a/include/cpphibernate/driver/mariadb/helper/key_properties.inl b/include/cpphibernate/driver/mariadb/helper/key_properties.inl index 6a22500..77ab59e 100644 --- a/include/cpphibernate/driver/mariadb/helper/key_properties.inl +++ b/include/cpphibernate/driver/mariadb/helper/key_properties.inl @@ -1,8 +1,8 @@ #pragma once -#include "key_properties.h" -#include + + namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/helper/nullable_helper.h b/include/cpphibernate/driver/mariadb/helper/nullable_helper.h index 3d74c1c..a0f8115 100644 --- a/include/cpphibernate/driver/mariadb/helper/nullable_helper.h +++ b/include/cpphibernate/driver/mariadb/helper/nullable_helper.h @@ -1,6 +1,6 @@ #pragma once -#include + namespace cpphibernate { namespace mariadb { @@ -31,5 +31,3 @@ namespace mariadb { }; } } - -#include "nullable_helper.inl" diff --git a/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl b/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl index 60a7164..e493538 100644 --- a/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl +++ b/include/cpphibernate/driver/mariadb/helper/nullable_helper.inl @@ -1,8 +1,8 @@ -#include "nullable_helper.h" -#include -#include + + + namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/helper/type_properties.h b/include/cpphibernate/driver/mariadb/helper/type_properties.h index a982bf1..af741ff 100644 --- a/include/cpphibernate/driver/mariadb/helper/type_properties.h +++ b/include/cpphibernate/driver/mariadb/helper/type_properties.h @@ -1,16 +1,10 @@ #pragma once -#include -#include +#include namespace cpphibernate { namespace mariadb { - /** - * @brief Value received from the database. - */ - using value_t = cppcore::nullable; - /** * @brief Type properties for the passed type. * @@ -56,5 +50,3 @@ namespace mariadb { }; } } - -#include "type_properties.inl" diff --git a/include/cpphibernate/driver/mariadb/helper/type_properties.inl b/include/cpphibernate/driver/mariadb/helper/type_properties.inl index e687358..bd6fb94 100644 --- a/include/cpphibernate/driver/mariadb/helper/type_properties.inl +++ b/include/cpphibernate/driver/mariadb/helper/type_properties.inl @@ -1,10 +1,6 @@ #pragma once -#include -#include - #include "type_properties.h" -#include "nullable_helper.h" namespace cpphibernate { namespace mariadb { diff --git a/include/cpphibernate/driver/mariadb/impl.h b/include/cpphibernate/driver/mariadb/impl.h index 4843de5..2d29b23 100644 --- a/include/cpphibernate/driver/mariadb/impl.h +++ b/include/cpphibernate/driver/mariadb/impl.h @@ -1,10 +1,9 @@ #pragma once -#include -#include -#include -#include -#include -#include +#include "impl/create_update.h" +#include "impl/driver_impl.h" +#include "impl/filter.h" -#include \ No newline at end of file +#include "impl/create_update.inl" +#include "impl/driver_impl.inl" +#include "impl/filter.inl" diff --git a/include/cpphibernate/driver/mariadb/impl/context.h b/include/cpphibernate/driver/mariadb/impl/_context.h similarity index 65% rename from include/cpphibernate/driver/mariadb/impl/context.h rename to include/cpphibernate/driver/mariadb/impl/_context.h index c40d858..b67f081 100644 --- a/include/cpphibernate/driver/mariadb/impl/context.h +++ b/include/cpphibernate/driver/mariadb/impl/_context.h @@ -1,8 +1,10 @@ #pragma once -#include -#include -#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 - 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 + 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 + 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 + 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 + struct change_context_builder; - protected: - template - inline void* set(T_dataset& dataset, size_t dataset_id = 0) const; + } - public: - template - 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 - 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>)) - , _dataset (&p_data) - , _table (nullptr) - { } - }; +#if 0 /* filter_context */ diff --git a/include/cpphibernate/driver/mariadb/impl/context.inl b/include/cpphibernate/driver/mariadb/impl/_context.inl similarity index 56% rename from include/cpphibernate/driver/mariadb/impl/context.inl rename to include/cpphibernate/driver/mariadb/impl/_context.inl index d30a5a3..59c06ce 100644 --- a/include/cpphibernate/driver/mariadb/impl/context.inl +++ b/include/cpphibernate/driver/mariadb/impl/_context.inl @@ -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 - inline void* data_context - ::set(T_dataset& dataset, size_t dataset_id) const - { - using dataset_type = mp::decay_t; - - _table = nullptr; - _dataset = &dataset; - _dataset_id = (dataset_id == 0) - ? misc::get_type_id(hana::type_c) - : 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 - inline decltype(auto) data_context - ::get() const + decltype(auto) data_context::get() const { using dataset_type = mp::decay_t; + /* 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); + + /* 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::name() + " is not a derived type of dataset with id " + std::to_string(_dataset_id)); } } + + /* return the dataset */ return *static_cast(_dataset); } + template + void data_context::set(T_dataset& dataset) + { + _dataset_id = misc::get_type_id(hana::type_c>); + _dataset = &dataset; + _table = nullptr; + } + + + + namespace __impl + { + + /* change_context_builder */ + + template + struct change_context_builder + { + template + static constexpr decltype(auto) apply(T_args&&...) + { static_assert(sizeof...(T_args) == -1, "Invalid parameters for change_context(...)!"); } + }; + + template + struct change_context_builder< + mp::list, + mp::enable_if_t< + mp::is_base_of_v>>> + { + 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 diff --git a/include/cpphibernate/driver/mariadb/impl/_destroy.h b/include/cpphibernate/driver/mariadb/impl/_destroy.h index add7eea..37d4b61 100644 --- a/include/cpphibernate/driver/mariadb/impl/_destroy.h +++ b/include/cpphibernate/driver/mariadb/impl/_destroy.h @@ -1,10 +1,10 @@ #pragma once -#include -#include -#include -#include -#include + + + + + beg_namespace_cpphibernate_driver_mariadb { @@ -93,4 +93,4 @@ beg_namespace_cpphibernate_driver_mariadb }; } -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +end_namespace_cpphibernate_driver_mariadb diff --git a/include/cpphibernate/driver/mariadb/impl/_limit.h b/include/cpphibernate/driver/mariadb/impl/_limit.h index 9076137..93c5caf 100644 --- a/include/cpphibernate/driver/mariadb/impl/_limit.h +++ b/include/cpphibernate/driver/mariadb/impl/_limit.h @@ -1,8 +1,8 @@ #pragma once -#include -#include -#include + + + beg_namespace_cpphibernate_driver_mariadb { diff --git a/include/cpphibernate/driver/mariadb/impl/_modifier_tags.h b/include/cpphibernate/driver/mariadb/impl/_modifier_tags.h index 020a4ff..cbf076d 100644 --- a/include/cpphibernate/driver/mariadb/impl/_modifier_tags.h +++ b/include/cpphibernate/driver/mariadb/impl/_modifier_tags.h @@ -1,7 +1,7 @@ #pragma once -#include -#include + + beg_namespace_cpphibernate_driver_mariadb { diff --git a/include/cpphibernate/driver/mariadb/impl/_order_by.h b/include/cpphibernate/driver/mariadb/impl/_order_by.h index cd85783..85e5976 100644 --- a/include/cpphibernate/driver/mariadb/impl/_order_by.h +++ b/include/cpphibernate/driver/mariadb/impl/_order_by.h @@ -1,9 +1,9 @@ #pragma once -#include -#include -#include -#include + + + + beg_namespace_cpphibernate_driver_mariadb { diff --git a/include/cpphibernate/driver/mariadb/impl/_read.h b/include/cpphibernate/driver/mariadb/impl/_read.h index 71cb715..59c546a 100644 --- a/include/cpphibernate/driver/mariadb/impl/_read.h +++ b/include/cpphibernate/driver/mariadb/impl/_read.h @@ -1,10 +1,10 @@ #pragma once -#include -#include -#include -#include -#include + + + + + 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 \ No newline at end of file +end_namespace_cpphibernate_driver_mariadb diff --git a/include/cpphibernate/driver/mariadb/impl/_where.h b/include/cpphibernate/driver/mariadb/impl/_where.h index a8ef2bc..5cd615f 100644 --- a/include/cpphibernate/driver/mariadb/impl/_where.h +++ b/include/cpphibernate/driver/mariadb/impl/_where.h @@ -1,9 +1,9 @@ #pragma once -#include -#include -#include -#include + + + + beg_namespace_cpphibernate_driver_mariadb { diff --git a/include/cpphibernate/driver/mariadb/impl/create_update.h b/include/cpphibernate/driver/mariadb/impl/create_update.h new file mode 100644 index 0000000..dec90a0 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/create_update.h @@ -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 + struct create_update_impl_t; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/_create_update.h b/include/cpphibernate/driver/mariadb/impl/create_update.inl similarity index 63% rename from include/cpphibernate/driver/mariadb/impl/_create_update.h rename to include/cpphibernate/driver/mariadb/impl/create_update.inl index 4b2f964..e2a5e10 100644 --- a/include/cpphibernate/driver/mariadb/impl/_create_update.h +++ b/include/cpphibernate/driver/mariadb/impl/create_update.inl @@ -1,47 +1,54 @@ #pragma once -#include -#include -#include -#include -#include -beg_namespace_cpphibernate_driver_mariadb -{ - /* create_update_impl_t */ - template +namespace cpphibernate { +namespace mariadb { + + /* create_update_impl_t - default (normal dataset) */ + + template 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); + auto dataset_id = get_type_id(hana::type_c); 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 struct create_update_impl_t< T_dataset, - mp::enable_if>> + mp::enable_if_t>> { using dataset_type = T_dataset; - using nullable_helper_type = misc::nullable_helper; + using nullable_helper_type = nullable_helper; 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 struct create_update_impl_t< T_dataset, - mp::enable_if>> + mp::enable_if_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& connection = context.connection; auto& dataset = context.get(); @@ -113,5 +122,4 @@ beg_namespace_cpphibernate_driver_mariadb } }; -} -end_namespace_cpphibernate_driver_mariadb \ No newline at end of file +} } diff --git a/include/cpphibernate/driver/mariadb/impl/driver_impl.h b/include/cpphibernate/driver/mariadb/impl/driver_impl.h index b0eaa6e..1c72c61 100644 --- a/include/cpphibernate/driver/mariadb/impl/driver_impl.h +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl.h @@ -1,8 +1,6 @@ #pragma once -#include - -#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 + inline void create(T_dataset& dataset) const; + /* } diff --git a/include/cpphibernate/driver/mariadb/impl/driver_impl.inl b/include/cpphibernate/driver/mariadb/impl/driver_impl.inl index 8e3ee46..e21a383 100644 --- a/include/cpphibernate/driver/mariadb/impl/driver_impl.inl +++ b/include/cpphibernate/driver/mariadb/impl/driver_impl.inl @@ -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 + 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::apply( + create_update_context(*schema, *connection, dataset)); + } + } } diff --git a/include/cpphibernate/driver/mariadb/impl/filter.h b/include/cpphibernate/driver/mariadb/impl/filter.h new file mode 100644 index 0000000..9c5b0c8 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/filter.h @@ -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; + using table_set_type = std::set; + + 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 + 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 + inline void set_exclusive(const schema_t& schema, T_args&&... args); + + /** + * @brief Clear the filter. + */ + inline void clear(); + }; + +} } diff --git a/include/cpphibernate/driver/mariadb/impl/filter.inl b/include/cpphibernate/driver/mariadb/impl/filter.inl new file mode 100644 index 0000000..b70333d --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/filter.inl @@ -0,0 +1,131 @@ +#pragma once + +#include + +#include "filter.h" +#include "../classes/fields/field.h" +#include "../classes/tables/table.h" +#include "../classes/schema/schema.h" + +#include +#include +#include + +namespace cpphibernate { +namespace mariadb { + + namespace __impl + { + + /* filter_add_element_impl */ + + template + struct filter_add_element_impl + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for filter_add_element(...)!"); } + }; + + template + struct filter_add_element_impl< + mp::list, + mp::enable_if_t< + schema::is_table_v>>> + { + 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 + struct filter_add_element_impl< + mp::list, + mp::enable_if_t< + schema::is_field_v>>> + { + 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>); + 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(tables.count(&table)); + if (!exclusive) + ret = !ret; + return ret; + } + + bool filter_t::is_excluded(const field_t& field) const + { + auto ret = static_cast(fields.count(&field)); + if (!exclusive) + ret = !ret; + return ret; + } + + template + void filter_t::set_inclusive(const schema_t& schema, T_args&&... args) + { + clear(); + exclusive = false; + cache_id = static_cast(cppcore::get_unique_id...>() + 1); + int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward(args)), void(), 0)... }; + (void)dummy; + } + + template + void filter_t::set_exclusive(const schema_t& schema, T_args&&... args) + { + clear(); + exclusive = true; + cache_id = -static_cast(cppcore::get_unique_id...>() + 1); + int dummy[] = { 0, (__impl::filter_add_element(*this, schema, std::forward(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(); + } + +} } diff --git a/include/cpphibernate/driver/mariadb/types.h b/include/cpphibernate/driver/mariadb/types.h new file mode 100644 index 0000000..61bac52 --- /dev/null +++ b/include/cpphibernate/driver/mariadb/types.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace cpphibernate { +namespace mariadb { + + /** + * @brief Value received from the database. + */ + using value_t = cppcore::nullable; + +} } diff --git a/include/cpphibernate/misc.h b/include/cpphibernate/misc.h index 0162724..1a581da 100644 --- a/include/cpphibernate/misc.h +++ b/include/cpphibernate/misc.h @@ -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" diff --git a/include/cpphibernate/modifier.h b/include/cpphibernate/modifier.h index 2d487c6..e323e54 100644 --- a/include/cpphibernate/modifier.h +++ b/include/cpphibernate/modifier.h @@ -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" diff --git a/include/cpphibernate/schema.h b/include/cpphibernate/schema.h index 638e4ca..177403f 100644 --- a/include/cpphibernate/schema.h +++ b/include/cpphibernate/schema.h @@ -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" diff --git a/include/cpphibernate/schema/field.h b/include/cpphibernate/schema/field.h index b3b85e5..01def1a 100644 --- a/include/cpphibernate/schema/field.h +++ b/include/cpphibernate/schema/field.h @@ -2,6 +2,7 @@ #include #include +#include namespace cpphibernate { namespace schema { diff --git a/include/cpphibernate/schema/table.h b/include/cpphibernate/schema/table.h index 9a53bf9..99f190d 100644 --- a/include/cpphibernate/schema/table.h +++ b/include/cpphibernate/schema/table.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include namespace cpphibernate { namespace schema { diff --git a/include/cpphibernate/types.h b/include/cpphibernate/types.h index 5e29d80..5cd92fc 100644 --- a/include/cpphibernate/types.h +++ b/include/cpphibernate/types.h @@ -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. * diff --git a/src/cpphibernate/driver/mariadb/classes/field.xcpp b/src/cpphibernate/driver/mariadb/classes/field.xcpp deleted file mode 100644 index f11e29f..0000000 --- a/src/cpphibernate/driver/mariadb/classes/field.xcpp +++ /dev/null @@ -1,282 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -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; -} \ No newline at end of file diff --git a/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp b/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp index 663621b..7e5129f 100644 --- a/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp +++ b/src/cpphibernate/driver/mariadb/classes/fields/misc.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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) diff --git a/src/cpphibernate/driver/mariadb/classes/schema.xcpp b/src/cpphibernate/driver/mariadb/classes/schema.xcpp deleted file mode 100644 index 4c321c6..0000000 --- a/src/cpphibernate/driver/mariadb/classes/schema.xcpp +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include - -using namespace ::utl; -using namespace ::cpphibernate::driver::mariadb_impl; - -void schema_t::update() -{ - // clear everything - for (auto& kvp : tables) - { - assert(static_cast(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(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(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(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(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(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(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 diff --git a/src/cpphibernate/driver/mariadb/classes/schema/init.cpp b/src/cpphibernate/driver/mariadb/classes/schema/init.cpp index 85f755d..e0605c0 100644 --- a/src/cpphibernate/driver/mariadb/classes/schema/init.cpp +++ b/src/cpphibernate/driver/mariadb/classes/schema/init.cpp @@ -1,4 +1,5 @@ -#include +#include +#include using namespace ::cpphibernate; using namespace ::cpphibernate::mariadb; diff --git a/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp b/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp index f89729e..5fd7e60 100644 --- a/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp +++ b/src/cpphibernate/driver/mariadb/classes/schema/misc.cpp @@ -1,8 +1,9 @@ +#include + +#include #include #include -#include - 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(t)); - _lookup.emplace(t->dataset_id, t.get()); + _table_lookup.emplace(t->dataset_id, t.get()); + + for (auto& f : t->fields) + { + assert(static_cast(f)); + _field_lookup.emplace(f->id, f.get()); + } } /* update table referencs */ @@ -40,16 +47,16 @@ void schema_t::init() auto& table = const_cast(*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(*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(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(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(it->second)); + return *it->second; +} diff --git a/src/cpphibernate/driver/mariadb/classes/table.xcpp b/src/cpphibernate/driver/mariadb/classes/table.xcpp deleted file mode 100644 index 7b0cf28..0000000 --- a/src/cpphibernate/driver/mariadb/classes/table.xcpp +++ /dev/null @@ -1,1827 +0,0 @@ -#include -#include - -#include -#include -#include - -#include -#include -#include - -using namespace ::utl; -using namespace ::cpphibernate; -using namespace ::cpphibernate::driver::mariadb_impl; - -/* data_extractor_t */ - -struct foreign_many_tuple_t -{ - const field_t& field; - read_context_ptr context; - std::string owner; -}; - -struct foreign_many_list_t : - public std::list - { }; - -struct data_extractor_t -{ - const table_t& _table; - const read_context& _context; - const ::cppmariadb::row& _row; - foreign_many_list_t& _foreign_many_list; - - const filter_t& _filter; - mutable size_t _index; - - data_extractor_t( - const table_t& p_table, - const read_context& p_context, - const ::cppmariadb::row& p_row, - foreign_many_list_t& p_foreign_many_list) - : _table (p_table) - , _context (p_context) - , _row (p_row) - , _foreign_many_list(p_foreign_many_list) - , _filter (p_context.filter) - , _index (0) - { } - - inline bool has_value() const - { return !_row.at(_index).is_null(); } - - inline value_t get_value() const - { - value_t ret; - auto f = _row.at(_index); - if (!f.is_null()) - ret = f.get(); - return ret; - } - - inline void next_field() const - { ++_index; } - - inline void read_field(const field_t& field, const read_context& context, bool skip = false) const - { - auto value = get_value(); - ++_index; - if (!skip) - field.set(context, value); - } - - inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const - { - /* read the base table */ - if (read_base && table.base_table) - { - skip = read_table(*table.base_table, context, true, false, skip); - } - - /* create a dynamic dataset depending on the derived table */ - else if ( read_base - && context.is_dynamic - && !table.derived_tables.empty()) - { - auto value = get_value(); - next_field(); - if (static_cast(value) && !skip) - { - auto type = utl::from_string(*value); - auto derived = _table.get_derived_by_table_id(type); - if (!derived) - throw misc::hibernate_exception(std::string("unable to find dereived table for id ") + std::to_string(type)); - derived->emplace(context); - } - else - { - skip = true; - } - } - - /* create a static dataset */ - else if (has_value() && !skip) - { - if (read_base) - { - context.emplace(); - } - } - - /* no data -> skip */ - else - { - skip = true; - } - - if (_context.filter.is_excluded(table)) - return skip; - - /* primary key */ - assert(table.primary_key_field); - read_field(*table.primary_key_field, context, skip); - - /* data fields */ - for (auto& ptr : table.data_fields) - { - assert(ptr); - auto& field = *ptr; - if (!_context.filter.is_excluded(field)) - read_field(field, context, skip); - } - - /* foreign table one */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(ptr); - assert(ptr->referenced_table); - - auto& field = *ptr; - auto& ref_table = *field.referenced_table; - - if ( _filter.is_excluded(field) - || _filter.is_excluded(ref_table)) - continue; - - auto next_context = field.foreign_read(context, skip); - assert(static_cast(next_context)); - read_table(ref_table, *next_context, true, true, skip); - next_context->finish(); - } - - /* foreign table many */ - if (!skip) - { - for (auto& ptr : table.foreign_table_many_fields) - { - assert(ptr); - assert(ptr->referenced_table); - - auto& field = *ptr; - auto& ref_table = *field.referenced_table; - - if ( _filter.is_excluded(field) - || _filter.is_excluded(ref_table)) - continue; - - _foreign_many_list.emplace_back( - foreign_many_tuple_t { - field, - field.foreign_read(context, false), - *table.primary_key_field->get(context), - }); - } - } - - /* derived tables */ - if (read_derived && context.is_dynamic) - { - for (auto& ptr : table.derived_tables) - { - assert(ptr); - auto& derived_table = *ptr; - read_table(derived_table, context, false, true, skip); - } - } - - return skip; - } - - inline void operator()() const - { - _index = 0; - read_table(_table, _context, true, true, false); - if (_index != _row.size()) - throw misc::hibernate_exception("result was not completely read!"); - } -}; - -/* select_query_builder_t */ - -struct select_query_builder_t -{ - struct local_context - { - const table_t& table; - std::string alias; - bool add_base; - bool add_derived; - bool is_dynamic; - }; - - const table_t& _table; - const filter_t& _filter; - bool _is_dynamic; - - size_t alias_id { 0 }; - size_t index { 0 }; - std::ostringstream os; - std::list joins; - - select_query_builder_t( - const table_t& p_table, - const filter_t& p_filter, - bool p_is_dynamic) - : _table (p_table) - , _filter (p_filter) - , _is_dynamic(p_is_dynamic) - { } - - inline std::string make_alias() - { return std::string("T") + std::to_string(alias_id++); } - - inline void add_field(const field_t& field, const std::string& alias) - { - if (index++) os << ", "; - os << field.convert_from_open - << "`" - << alias - << "`.`" - << field.field_name - << "`" - << field.convert_from_close; - } - - inline bool add_table(const local_context& ctx) - { - bool ret = false; - auto has_alias = !ctx.alias.empty(); - auto real_alias = has_alias ? ctx.alias : ctx.table.table_name; - - if (ctx.table.base_table && ctx.add_base) - { - assert(ctx.table.base_table->primary_key_field); - auto& base_table = *ctx.table.base_table; - auto& base_key = *base_table.primary_key_field; - auto base_alias = has_alias ? make_alias() : std::string(); - auto real_base_alias = has_alias ? base_alias : base_table.table_name; - - std::ostringstream ss; - ss << " JOIN `" - << base_table.table_name; - if (has_alias) - { - ss << "` AS `" - << base_alias; - } - ss << "` ON `" - << real_alias - << "`.`" - << base_key.field_name - << "`=`" - << real_base_alias - << "`.`" - << base_key.field_name - << "`"; - - auto it = joins.insert(joins.end(), ss.str()); - if (add_table({ - base_table, - base_alias, - true, - false, - ctx.is_dynamic - })) - { - ret = true; - } - else - { - joins.erase(it); - } - } - - /* __type */ - if ( ctx.is_dynamic - && !ctx.table.base_table - && !ctx.table.derived_tables.empty()) - { - if (index++) os << ", "; - os << "`" - << ctx.table.table_name - << "`.`__type` AS `__type`"; - ret = true; - } - - if (_filter.is_excluded(ctx.table)) - return ret; - - /* primary key */ - assert(ctx.table.primary_key_field); - add_field(*ctx.table.primary_key_field, real_alias); - ret = true; - - /* data fields */ - for (auto& ptr : ctx.table.data_fields) - { - assert(ptr); - auto& field = *ptr; - if (!_filter.is_excluded(field)) - { - add_field(field, real_alias); - } - } - - /* foreign table one */ - for (auto& ptr : ctx.table.foreign_table_one_fields) - { - assert(ptr); - assert(ptr->table); - assert(ptr->table->primary_key_field); - assert(ptr->referenced_table); - assert(ptr->referenced_table->primary_key_field); - - auto& field = *ptr; - auto& table = *field.table; - auto& own_key = *table.primary_key_field; - auto& ref_table = *field.referenced_table; - auto& ref_key = *ref_table.primary_key_field; - - if ( _filter.is_excluded(field) - || _filter.is_excluded(ref_table)) - continue; - - auto new_alias = make_alias(); - - std::ostringstream ss; - if (ref_table.is_used_in_container) - { - ss << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << own_key.field_name - << "`=`" - << new_alias - << "`.`" - << table.table_name - << "_id_" - << field.field_name - << "`"; - } - else - { - ss << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << ref_key.table_name - << "_id_" - << field.field_name - << "`=`" - << new_alias - << "`.`" - << ref_key.field_name - << "`"; - } - - auto it = joins.insert(joins.end(), ss.str()); - if (!add_table({ - ref_table, - new_alias, - true, - true, - field.value_is_pointer - })) - { - joins.erase(it); - } - } - - /* derived tables */ - if (ctx.add_derived && ctx.is_dynamic) - { - for (auto& ptr : ctx.table.derived_tables) - { - assert(ptr); - assert(ptr->primary_key_field); - auto& derived_table = *ptr; - auto& primary_key = *ctx.table.primary_key_field; - auto derived_alias = has_alias ? make_alias() : std::string(); - auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name; - - std::ostringstream ss; - ss << " LEFT JOIN `" - << derived_table.table_name; - if (has_alias) - { - ss << "` AS `" - << derived_alias; - } - ss << "` ON `" - << real_alias - << "`.`" - << primary_key.field_name - << "`=`" - << real_derived_alias - << "`.`" - << primary_key.field_name - << "`"; - - auto it = joins.insert(joins.end(), ss.str()); - if (!add_table({ - derived_table, - derived_alias, - false, - true, - ctx.is_dynamic, - })) - { - joins.erase(it); - } - } - } - - return ret; - } - - inline std::string operator()() - { - os << "SELECT "; - add_table({ - _table, - "", - true, - true, - _is_dynamic, - }); - os << " FROM `" - << _table.table_name - << "`"; - - for (auto& join : joins) - os << join; - - os << " ?where! ?order! ?limit!"; - return os.str(); - } -}; - -/* delete_query_builder_t */ - -struct delete_query_builder_t -{ - const table_t& _table; - - size_t alias_id { 0 }; - std::ostringstream os; - std::list names; - - delete_query_builder_t( - const table_t& p_table) - : _table (p_table) - { } - - inline std::string make_alias() - { return std::string("T") + std::to_string(alias_id++); } - - inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) - { - auto has_alias = !alias.empty(); - auto real_alias = has_alias ? alias : table.table_name; - - if (table.base_table && add_base) - { - assert(table.base_table->primary_key_field); - - auto& base_table = *table.base_table; - auto& base_key = *base_table.primary_key_field; - auto base_alias = has_alias ? make_alias() : std::string(); - auto real_base_alias = has_alias ? base_alias : base_table.table_name; - - os << " LEFT JOIN `" - << base_table.table_name; - if (has_alias) - { - os << "` AS `" - << base_alias; - } - os << "` ON `" - << real_alias - << "`.`" - << base_key.field_name - << "`=`" - << real_base_alias - << "`.`" - << base_key.field_name - << "`"; - - add_table(base_table, base_alias, true, false); - } - - names.emplace_back(real_alias); - - /* foreign table one */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(ptr); - assert(ptr->table); - assert(ptr->table->primary_key_field); - assert(ptr->referenced_table); - assert(ptr->referenced_table->primary_key_field); - - auto& field = *ptr; - auto& own_key = *table.primary_key_field; - auto& ref_table = *field.referenced_table; - auto& ref_key = *ref_table.primary_key_field; - auto new_alias = make_alias(); - - if (ref_table.is_used_in_container) - { - os << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << own_key.field_name - << "`=`" - << new_alias - << "`.`" - << table.table_name - << "_id_" - << field.field_name - << "`"; - } - else - { - os << " LEFT JOIN `" - << ref_table.table_name - << "` AS `" - << new_alias - << "` ON `" - << real_alias - << "`.`" - << ref_key.table_name - << "_id_" - << field.field_name - << "`=`" - << new_alias - << "`.`" - << ref_key.field_name - << "`"; - } - - add_table(ref_table, new_alias, true, true); - } - - /* derived tables */ - if (add_derived) - { - for (auto& ptr : table.derived_tables) - { - assert(ptr); - assert(ptr->primary_key_field); - - auto& derived_table = *ptr; - auto& primary_key = *table.primary_key_field; - auto derived_alias = has_alias ? make_alias() : std::string(); - auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name; - - os << " LEFT JOIN `" - << derived_table.table_name; - if (has_alias) - { - os << "` AS `" - << derived_alias; - } - os << "` ON `" - << real_alias - << "`.`" - << primary_key.field_name - << "`=`" - << real_derived_alias - << "`.`" - << primary_key.field_name - << "`"; - - add_table(derived_table, derived_alias, false, true); - } - } - } - - inline std::string operator()(const std::string* where) - { - os << " FROM `" - << _table.table_name - << "`"; - add_table(_table, "", true, true); - if (where) - { - os << " " << *where; - } - else - { - os << " ?where!"; - } - - std::ostringstream ss; - ss << "DELETE "; - bool first = true; - for (auto& name : names) - { - if (first) - first = false; - else - ss << ", "; - ss << "`" - << name - << "`"; - } - ss << os.str(); - - return ss.str(); - } -}; - -/* build queries */ - -std::string build_init_stage1_query(const table_t& table) -{ - std::ostringstream os; - - /* CREATE TABLE */ - os << "CREATE TABLE IF NOT EXISTS `" - << table.table_name - << "`" - << indent - << "(" - << incindent; - - /* primary key */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - auto args = key_info.create_arguments; - os << indent - << "`" - << key_info.field_name - << "` " - << key_info.type - << " NOT NULL" - << (args.empty() ? "" : " ") - << args - << ","; - } - - /* base table key fields */ - if (static_cast(table.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; - os << indent - << "`" - << key_info.field_name - << "` " - << key_info.type - << " NOT NULL,"; - } - - /* foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - os << indent - << "`" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` " - << ref_key_info.type - << (field_info.value_is_nullable - ? " NULL DEFAULT NULL," - : " NOT NULL,"); - } - - /* foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& ref_key_info = *field_info.table->primary_key_field; - os << indent - << "`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` " - << ref_key_info.type - << " NULL DEFAULT NULL,"; - if (field_info.value_is_ordered) - { - os << indent - << "`" - << field_info.table_name - << "_index_" - << field_info.field_name - << "` INT UNSIGNED NOT NULL,"; - } - } - - /* data fields */ - for (auto& ptr : table.data_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - os << indent - << "`" - << field_info.field_name - << "` " - << field_info.type - << (field_info.value_is_nullable - ? " NULL DEFAULT NULL," - : " NOT NULL,"); - } - - /* type field for derived tables */ - if (!table.derived_tables.empty() && - !table.base_table) - { - os << indent - << "`__type` INT UNSIGNED NOT NULL,"; - } - - /* PRIMARY KEY */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - os << indent - << "PRIMARY KEY ( `" - << key_info.field_name - << "` )"; - } - - /* UNIQUE INDEX primary key */ - { - assert(table.primary_key_field); - auto& key_info = *table.primary_key_field; - os << ',' - << indent - << "UNIQUE INDEX `index_" - << key_info.field_name - << "` ( `" - << key_info.field_name - << "` ASC )"; - } - - /* UNIQUE INDEX base table keys */ - if (table.base_table) - { - auto& table_info = *table.base_table; - assert(table_info.primary_key_field); - auto& key_info = *table_info.primary_key_field; - os << ',' - << indent - << "UNIQUE INDEX `index_" - << key_info.field_name - << "` ( `" - << key_info.field_name - << "` ASC )"; - } - - /* INDEX foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - os << "," - << indent - << "INDEX `index_" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` ( `" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "` ASC )"; - } - - /* INDEX foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - os << "," - << indent - << "INDEX `index_" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` ( `" - << field_info.table_name - << "_id_" - << field_info.field_name - << "` ASC )"; - } - - /* CREATE TABLE end */ - os << decindent - << indent - << ")" - << indent - << "ENGINE = InnoDB" - << indent - << "DEFAULT CHARACTER SET = utf8"; - - return os.str(); -} - -std::string build_init_stage2_query(const table_t& table) -{ - std::ostringstream os; - - /* ALTER TABLE */ - os << "ALTER TABLE `" - << table.table_name - << "`" - << incindent; - - size_t index = 0; - - /* CONSTRAINT base table */ - if (table.base_table) - { - 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_" - << table.table_name - << "_" - << ref_key_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << ref_key_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent - << "ON DELETE CASCADE" - << indent - << "ON UPDATE CASCADE" - << decindent; - } - - /* CONSTRAINT foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.referenced_table); - assert(field_info.referenced_table->primary_key_field); - if (field_info.referenced_table->is_used_in_container) - continue; - auto& ref_key_info = *field_info.referenced_table->primary_key_field; - if (index++) os << ","; - os << indent - << "ADD CONSTRAINT `fk_" - << table.table_name - << "_" - << ref_key_info.table_name - << "_" - << field_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << ref_key_info.table_name - << "_id_" - << field_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent; - if (field_info.value_is_nullable) - os << "ON DELETE SET NULL"; - else - os << "ON DELETE CASCADE"; - os << indent - << "ON UPDATE NO ACTION" - << decindent; - } - - /* CONSTRAINT foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& ref_key_info = *field_info.table->primary_key_field; - if (index++) os << ","; - os << indent - << "ADD CONSTRAINT `fk_" - << table.table_name - << "_" - << field_info.table_name - << "_" - << field_info.field_name - << "`" - << incindent - << indent - << "FOREIGN KEY IF NOT EXISTS (`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "`)" - << indent - << "REFERENCES `" - << ref_key_info.schema_name - << "`.`" - << ref_key_info.table_name - << "` (`" - << ref_key_info.field_name - << "`)" - << indent - << "ON DELETE SET NULL" - << indent - << "ON UPDATE NO ACTION" - << decindent; - } - - return index == 0 - ? std::string { } - : os.str(); -} - -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(filter); - - /* INSER INTO / UPDATE */ - os << (is_update - ? "UPDATE" - : "INSERT INTO") - << " `" - << table.table_name - << "`"; - - /* primary key */ - if (!is_update) - { - 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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - } - - /* base table key fields */ - if ( static_cast(table.base_table) - && ( !is_update - || !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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - - /* foreign table one fields */ - for (auto& ptr : table.foreign_table_one_fields) - { - assert(static_cast(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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.table_name - << "_id_" - << field_info.field_name - << "?" - << key_info.convert_to_close; - } - - /* foreign fields */ - for (auto& ptr : table.foreign_key_fields) - { - assert(static_cast(ptr)); - if (is_update && ptr != owner) - continue; - if (index++) - os << ", "; - else - os << " SET "; - auto& field_info = *ptr; - assert(field_info.table); - assert(field_info.table->primary_key_field); - auto& key_info = *field_info.table->primary_key_field; - os << "`" - << field_info.table_name - << "_id_" - << field_info.field_name - << "`=" - << key_info.convert_to_open - << "?" - << field_info.table_name - << "_id_" - << field_info.field_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.field_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.field_name - << "`=" - << field_info.convert_to_open - << "?" - << field_info.field_name - << "?" - << field_info.convert_to_close; - } - - /* type field for derived tables */ - if ( !table.derived_tables.empty() - && !table.base_table - && !is_update) - { - 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.field_name - << "`=" - << key_info.convert_to_open - << "?" - << key_info.field_name - << "?" - << key_info.convert_to_close; - } - - return index == 0 && !(table.primary_key_field->value_is_auto_incremented && !is_update) - ? std::string() - : os.str(); -} - -std::string build_select_query( - const table_t& table, - const filter_t& filter, - bool is_dynamic) -{ - return select_query_builder_t(table, filter, is_dynamic)(); -} - -/* execute_create_update */ - -std::string table_t::execute_create_update( - const create_update_context& context, - ::cppmariadb::statement* statement) const -{ - auto& connection = context.connection; - auto& filter = context.filter; - - size_t index = 0; - bool is_update = context.is_update; - - std::string primary_key; - if (statement) statement->clear(); - - /* primary key */ - assert(primary_key_field); - if (is_update) - { - primary_key = get_primary_key(context); - } - else if (!primary_key_field->value_is_auto_incremented) - { - primary_key = primary_key_field->generate_value(context.connection); - if (statement) statement->set(index, primary_key); - ++index; - } - - /* base_key */ - if ( base_table - && ( !is_update - || !filter.is_excluded(*base_table))) - { - auto new_context = context; - if (!new_context.derived_table) - new_context.derived_table = this; - std::string key = create_update_base(new_context); - if (statement) statement->set(index, std::move(key)); - ++index; - } - - if (is_update && filter.is_excluded(*this)) - return primary_key; - - /* foreign table one fields */ - for (auto& ptr : 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, std::move(key)); - } - else if (field.value_is_nullable) - { - if (statement) statement->set_null(index); - } - else - { - throw misc::hibernate_exception("Received null key for non nullable foreign dataset!"); - } - ++index; - - /* cleanup old dataset (if new one was created) */ - if (context.is_update) - { - 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_debug_log("execute DELETE old foreign one query: " << delete_statement.query(connection)); - connection.execute(delete_statement); - - table_set processed; - field.referenced_table->destroy_cleanup(context, processed, true, true); - } - } - - /* foreign fields */ - for (auto& ptr : 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 : 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 ( !derived_tables.empty() - && !base_table - && !is_update) - { - if (statement) statement->set(index, context.derived_table - ? context.derived_table->table_id - : table_id); - ++index; - } - - /* where primary key (for update) */ - if (is_update) - { - assert(primary_key_field); - if (statement) statement->set(index, *primary_key_field->get(context)); - ++index; - } - - /* execute */ - if (statement) - { - if (!is_update) - { - cpphibernate_debug_log("execute INSERT query: " << statement->query(connection)); - } - else - { - cpphibernate_debug_log("execute UPDATE query: " << statement->query(connection)); - } - - if ( primary_key_field->value_is_auto_incremented - && !is_update) - { - auto id = connection.execute_id(*statement); - primary_key = utl::to_string(id); - } - else - { - auto count = connection.execute_rows(*statement); - if (count > 1) - throw misc::hibernate_exception("Expected one/ row to be inserted/updated!"); - cpphibernate_debug_log(count << " rows inserted/updated"); - } - primary_key_field->set(context, primary_key); - } - - /* foreign table many fields */ - for (auto& ptr : 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) - { - 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) - { - table_set processed; - ref_table.destroy_cleanup(context, processed, true, true); - } - } - - return primary_key; -} - -/* table_t */ - -void table_t::print(std::ostream& os) const -{ - os << indent << '{' - << incindent - << indent << "\"dataset_id\": " << dataset_id << "," - << indent << "\"base_dataset_id\": " << base_dataset_id << "," - << indent << "\"table_id\": " << table_id << "," - << indent << "\"derived_dataset_ids\": " << misc::print_container(derived_dataset_ids, false) << "," - << indent << "\"schema_name\": \"" << schema_name << "\"," - << indent << "\"table_name\": \"" << table_name << "\"," - << indent << "\"fields\":" << misc::print_container(fields, true, [](auto& s, auto& field) { - field->print(s); - }) << "," - << indent << "\"base_table\": " << (base_table ? std::string("\"") + base_table->table_name + "\"" : "null") << "," - << indent << "\"derived_tables\":" << misc::print_container(derived_tables, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->table_name << '"'; - }) << "," - << indent << "\"primary_key_field\": " << (primary_key_field ? std::string("\"") + primary_key_field->field_name + "\"" : "null") << "," - << indent << "\"foreign_key_fields\": " << misc::print_container(foreign_key_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->table_name << '.' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_fields\": " << misc::print_container(foreign_table_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_one_fields\": " << misc::print_container(foreign_table_one_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"foreign_table_many_fields\": " << misc::print_container(foreign_table_many_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) << "," - << indent << "\"data_fields\": " << misc::print_container(data_fields, true, [](auto& s, auto& ptr){ - s << indent << '"' << ptr->field_name << '"'; - }) - << decindent - << indent << '}'; -} - -const table_t* table_t::get_derived_by_table_id(size_t id) const -{ - if (table_id == id) - return this; - for (auto ptr : derived_tables) - { - assert(ptr); - auto ret = ptr->get_derived_by_table_id(id); - if (ret) return ret; - } - return nullptr; -} - -const table_t* table_t::get_derived_by_dataset_id(size_t id) const -{ - if (dataset_id == id) - return this; - for (auto ptr : derived_tables) - { - assert(ptr); - auto ret = ptr->get_derived_by_dataset_id(id); - if (ret) return ret; - } - return nullptr; -} - -void table_t::emplace(const read_context& context) const - { throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); } - -std::string table_t::get_where_primary_key(const data_context& context) const -{ - assert(primary_key_field); - - auto& field = *primary_key_field; - auto primary_key = *field.get(context); - - std::ostringstream os; - os << "WHERE `" - << field.table_name - << "`.`" - << field.field_name - << "`=" - << field.convert_to_open - << "'" - << context.connection.escape(primary_key) - << "'" - << field.convert_to_close; - - return os.str(); -} - -std::string table_t::build_delete_query(const std::string* where) const - { return delete_query_builder_t(*this)(where); } - -::cppmariadb::statement& table_t::get_statement_key_from_base() const -{ - if (!_statement_key_from_base) - { - if (!base_table) - throw exception(std::string("table has no base table: ") + 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.field_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; -} - -::cppmariadb::statement& table_t::get_statement_create_table() const -{ - if (_statement_create_table) - return *_statement_create_table; - auto query = build_init_stage1_query(*this); - _statement_create_table.reset(new ::cppmariadb::statement(query)); - return *_statement_create_table; -} - -::cppmariadb::statement* table_t::get_statement_alter_table() const -{ - if (!_statement_alter_table) - { - auto query = build_init_stage2_query(*this); - _statement_alter_table.reset(new ::cppmariadb::statement(query)); - } - return _statement_alter_table->empty() - ? nullptr - : _statement_alter_table.get(); -} - -::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_select(const read_context& context) const -{ - auto& map = context.is_dynamic - ? _statement_select_dynamic - : _statement_select_static; - auto key = std::make_tuple(context.filter.cache_id, static_cast(nullptr)); - auto it = map.find(key); - if (it == map.end()) - { - auto query = build_select_query(*this, context.filter, context.is_dynamic); - it = map.emplace(key, ::cppmariadb::statement(query)).first; - } - return it->second; -} - -::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; -} - -::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const -{ - if (!_statement_foreign_many_delete) - { - std::ostringstream os; - auto first = true; - os << "WHERE"; - for (auto ptr : foreign_key_fields) - { - assert(ptr); - auto& field = *ptr; - if (first) - first = false; - else - os << " AND"; - os << " (`" - << field.table_name - << "_id_" - << field.field_name - << "` IS NULL)"; - } - std::string where = os.str(); - - auto query = delete_query_builder_t(*this)(&where); - _statement_foreign_many_delete.reset(new ::cppmariadb::statement(query)); - } - return *_statement_foreign_many_delete; -} - -::cppmariadb::statement& table_t::get_statement_delete() const -{ - if (!_statement_delete) - { - auto query = delete_query_builder_t(*this)(nullptr); - _statement_delete.reset(new ::cppmariadb::statement(query)); - } - return *_statement_delete; -} - -void table_t::execute_foreign_many_delete(const base_context& context) const -{ - if (foreign_key_fields.empty()) - return; - auto& connection = context.connection; - auto& statement = get_statement_foreign_many_delete(); - cpphibernate_debug_log("execute DELETE old foreign many query: " << statement.query(connection)); - connection.execute(statement); -} - -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: ") + 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 table_t::create_update_base(const create_update_context& context) const -{ - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "'" << this->table_name << "' does not implement create_update_base!").str()); -} - -void table_t::init_stage1_exec(const init_context& context) const -{ - auto& statement = get_statement_create_table(); - auto& connection = context.connection; - cpphibernate_debug_log("execute CREATE TABLE query: " << statement.query(connection)); - connection.execute(statement); -} - -void table_t::init_stage2_exec(const init_context& context) const -{ - auto* statement = get_statement_alter_table(); - auto& connection = context.connection; - if (!statement) return; - cpphibernate_debug_log("execute ALTER TABLE query: " << statement->query(connection)); - connection.execute(*statement); -} - -std::string table_t::create_update_intern(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(context, statement); -} - -void table_t::read_exec(const read_context& context) const -{ - auto& statement = get_statement_select(context); - auto& connection = context.connection; - - statement.set(0, context.where); - statement.set(1, context.order_by); - statement.set(2, context.limit); - - cpphibernate_debug_log("execute SELECT query: " << statement.query(connection)); - auto result = connection.execute_used(statement); - if (!result) - throw misc::hibernate_exception("Unable to fetching data from database!"); - - ::cppmariadb::row* row; - foreign_many_list_t foreign_many_list; - while ((row = result->next())) - data_extractor_t(*this, context, *row, foreign_many_list)(); - context.finish(); - - for (auto& tuple : foreign_many_list) - { - auto& field = tuple.field; - auto& next_context = *tuple.context; - assert(field.table); - assert(field.referenced_table); - assert(field.referenced_table->primary_key_field); - auto& ref_table = *field.referenced_table; - auto& ref_field = *ref_table.primary_key_field; - - { - std::ostringstream ss; - ss << "WHERE (`" - << ref_table.table_name - << "`.`" - << field.table_name - << "_id_" - << field.field_name - << "`=" - << ref_field.convert_to_open - << "'" - << context.connection.escape(tuple.owner) - << "'" - << ref_field.convert_to_close - << ")"; - next_context.where = ss.str(); - } - - { - std::ostringstream ss; - ss << "ORDER BY `" - << ref_table.table_name - << "`.`" - << field.table_name - << "_index_" - << field.field_name - << "` ASC"; - next_context.order_by = ss.str(); - } - - ref_table.read(next_context); - } -} - -void table_t::destroy_intern(const destroy_context& context) const - { return destroy_exec(context); } - -void table_t::destroy_exec(const destroy_context& context) const -{ - assert(primary_key_field); - - auto& connection = context.connection; - auto& statement = get_statement_delete(); - - statement.set(0, context.where); - cpphibernate_debug_log("execute DELETE query: " << statement.query(connection)); - connection.execute(statement); - - table_set processed; - for (auto& ptr : foreign_table_fields) - { - assert(ptr); - assert(ptr->referenced_table); - auto& ref_table = *ptr->referenced_table; - if (ref_table.is_used_in_container) - ref_table.destroy_cleanup(context, processed, true, true); - } -} - -void table_t::destroy_cleanup(const base_context& context, table_set& processed, bool check_derived, bool check_base) const -{ - if (processed.count(this)) - return; - - processed.emplace(this); - - execute_foreign_many_delete(context); - - for (auto ptr : foreign_table_fields) - { - assert(ptr); - assert(ptr->referenced_table); - auto& ref_table = *ptr->referenced_table; - if (ref_table.is_used_in_container) - ref_table.destroy_cleanup(context, processed, true, true); - } - - if (check_base && base_table) - { - base_table->destroy_cleanup(context, processed, false, true); - } - - if (check_derived) - { - for (auto ptr : derived_tables) - { - assert(ptr); - ptr->destroy_cleanup(context, processed, true, false); - } - } -} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp b/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp new file mode 100644 index 0000000..a243c6d --- /dev/null +++ b/src/cpphibernate/driver/mariadb/classes/tables/create_update.cpp @@ -0,0 +1,483 @@ +#include + +#include +#include +#include +#include + +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(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(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(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(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; +} diff --git a/src/cpphibernate/driver/mariadb/classes/tables/init.cpp b/src/cpphibernate/driver/mariadb/classes/tables/init.cpp index 5004097..9df25bc 100644 --- a/src/cpphibernate/driver/mariadb/classes/tables/init.cpp +++ b/src/cpphibernate/driver/mariadb/classes/tables/init.cpp @@ -1,10 +1,16 @@ #include -#include -#include + +#include +#include +#include +#include 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(base_table)) + if (static_cast(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(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(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(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(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(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(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(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(); } diff --git a/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp b/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp index 80d7e03..0957d3f 100644 --- a/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp +++ b/src/cpphibernate/driver/mariadb/classes/tables/misc.cpp @@ -3,6 +3,9 @@ #include +#include +#include + 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(); +} + +::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(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; } diff --git a/test/cpphibernate/cpphibernate_create.xcpp b/test/cpphibernate/cpphibernate_create.cpp similarity index 99% rename from test/cpphibernate/cpphibernate_create.xcpp rename to test/cpphibernate/cpphibernate_create.cpp index 37b74da..e3e2f23 100644 --- a/test/cpphibernate/cpphibernate_create.xcpp +++ b/test/cpphibernate/cpphibernate_create.cpp @@ -45,10 +45,10 @@ TEST(CppHibernateTests, create_test1) t1.u32_ptr_s = std::make_shared(789); ::cppmariadb::connection connection(reinterpret_cast(0x1111)); - auto context = make_context(test_schema, connection); + auto context = make_context(test_schema, connection); context.create(t1); } - +#if 0 TEST(CppHibernateTests, create_test2) { StrictMock 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); -} \ No newline at end of file +} +#endif diff --git a/test/cpphibernate/cpphibernate_init.cpp b/test/cpphibernate/cpphibernate_init.xcpp similarity index 100% rename from test/cpphibernate/cpphibernate_init.cpp rename to test/cpphibernate/cpphibernate_init.xcpp