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