| @@ -195,5 +195,24 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| using read_context_ptr = std::unique_ptr<read_context>; | |||
| /* destroy_context */ | |||
| struct destroy_context | |||
| : public data_context | |||
| { | |||
| std::string where; | |||
| template<typename T_data> | |||
| inline destroy_context( | |||
| T_data& p_data, | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection) | |||
| : data_context( | |||
| p_data, | |||
| p_schema, | |||
| p_connection) | |||
| { } | |||
| }; | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -1,6 +1,7 @@ | |||
| #pragma once | |||
| #include <cpphibernate/driver/mariadb/impl/create_update.h> | |||
| #include <cpphibernate/driver/mariadb/impl/destroy.h> | |||
| #include <cpphibernate/driver/mariadb/impl/limit.h> | |||
| #include <cpphibernate/driver/mariadb/impl/order_by.h> | |||
| #include <cpphibernate/driver/mariadb/impl/read.h> | |||
| @@ -0,0 +1,96 @@ | |||
| #pragma once | |||
| #include <cppmariadb.h> | |||
| #include <cpphibernate/misc.h> | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/driver/mariadb/helper.h> | |||
| #include <cpphibernate/driver/mariadb/schema/schema.h> | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| /* destroy_impl_t */ | |||
| template<typename T_dataset, typename = void> | |||
| struct destroy_impl_t | |||
| { | |||
| using dataset_type = T_dataset; | |||
| static inline void apply(const destroy_context& context, bool strict = true) | |||
| { | |||
| auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
| auto& connection = context.connection; | |||
| auto& schema = context.schema; | |||
| auto& table = schema.table(dataset_id); | |||
| assert(table.primary_key_field); | |||
| transaction_lock trans(connection); | |||
| if (!table.primary_key_field->is_default(context)) | |||
| { | |||
| table.destroy(context); | |||
| } | |||
| else if (strict) | |||
| { | |||
| throw misc::hibernate_exception("can not destroy dataset with no primary key assigned!"); | |||
| } | |||
| trans.commit(); | |||
| } | |||
| }; | |||
| /* destroy_impl_t - nullable */ | |||
| template<typename T_dataset> | |||
| struct destroy_impl_t< | |||
| T_dataset, | |||
| mp::enable_if<misc::is_nullable<T_dataset>>> | |||
| { | |||
| using dataset_type = T_dataset; | |||
| using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||
| static inline void apply(const destroy_context& context, bool strict = true) | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| auto* value = nullable_helper_type::get(dataset); | |||
| if (value) | |||
| { | |||
| using new_dataset_type = mp::decay_t<decltype(*value)>; | |||
| using new_destroy_impl_type = destroy_impl_t<new_dataset_type>; | |||
| auto new_context = change_context(context, *value); | |||
| new_destroy_impl_type::apply(new_context, strict); | |||
| } | |||
| else if (strict) | |||
| { | |||
| throw misc::hibernate_exception("can not destroy nullable type with no value!"); | |||
| } | |||
| } | |||
| }; | |||
| /* destroy_impl_t - container */ | |||
| template<typename T_dataset> | |||
| struct destroy_impl_t< | |||
| T_dataset, | |||
| mp::enable_if<misc::is_container<T_dataset>>> | |||
| { | |||
| using dataset_type = T_dataset; | |||
| static inline void apply(const destroy_context& context, bool strict = true) | |||
| { | |||
| auto& connection = context.connection; | |||
| auto& dataset = context.get<dataset_type>(); | |||
| transaction_lock trans(connection); | |||
| for (auto& x : dataset) | |||
| { | |||
| using new_dataset_type = mp::decay_t<decltype(x)>; | |||
| using new_destroy_impl_type = destroy_impl_t<new_dataset_type>; | |||
| auto new_context = change_context(context, x); | |||
| new_destroy_impl_type::apply(new_context, strict); | |||
| } | |||
| trans.commit(); | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -65,6 +65,21 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| create_update_impl_t<T_dataset>::apply( | |||
| create_update_context(dataset, _schema, _connection, _filter, true)); | |||
| } | |||
| template<typename T_dataset> | |||
| inline void destroy_impl(T_dataset& dataset) const | |||
| { | |||
| using dataset_type = mp::decay_t<T_dataset>; | |||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
| auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
| auto& table = _schema.table(dataset_id); | |||
| destroy_context context(dataset, _schema, _connection); | |||
| context.where = table.get_where_primary_key(context); | |||
| destroy_impl_t<T_dataset>::apply(context); | |||
| } | |||
| }; | |||
| } | |||
| @@ -67,7 +67,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| const table_t* get_derived_by_table_id(size_t id) const; | |||
| const table_t* get_derived_by_dataset_id(size_t id) const; | |||
| virtual void emplace(const read_context& context) const; | |||
| virtual void emplace (const read_context& context) const; | |||
| std::string get_where_primary_key(const data_context& context) const; | |||
| std::string build_delete_query (const std::string* where) const; | |||
| /* CRUD */ | |||
| inline void init_stage1(const init_context& context) const | |||
| @@ -82,6 +84,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| inline void read(const read_context& context) const | |||
| { return read_exec(context); } | |||
| inline void destroy(const destroy_context& context) const | |||
| { return destroy_intern(context); } | |||
| private: | |||
| template<typename T_schema, typename T_table, typename T_base_dataset> | |||
| friend struct table_simple_t; | |||
| @@ -100,6 +105,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| mutable statement_map _statement_select_dynamic; | |||
| mutable statement_map _statement_update; | |||
| mutable statement_ptr _statement_foreign_many_delete; | |||
| mutable statement_ptr _statement_delete; | |||
| ::cppmariadb::statement& get_statement_create_table() const; | |||
| ::cppmariadb::statement* get_statement_alter_table() const; | |||
| @@ -107,6 +113,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| ::cppmariadb::statement& get_statement_select(const read_context& context) const; | |||
| ::cppmariadb::statement& get_statement_update(const filter_t& filter, const field_t* owner) const; | |||
| ::cppmariadb::statement& get_statement_foreign_many_delete() const; | |||
| ::cppmariadb::statement& get_statement_delete() const; | |||
| void execute_foreign_many_delete(const base_context& context) const; | |||
| std::string execute_create_update( | |||
| const create_update_context& context, | |||
| @@ -122,6 +131,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| std::string create_update_exec (const create_update_context& context) const; | |||
| void read_exec (const read_context& context) const; | |||
| virtual void destroy_intern (const destroy_context& context) const; | |||
| void destroy_exec (const destroy_context& context) const; | |||
| void destroy_cleanup (const base_context& context, bool check_derived, bool check_base) const; | |||
| }; | |||
| /* table_simple_t */ | |||
| @@ -177,6 +190,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| protected: | |||
| virtual std::string create_update_intern(const create_update_context& context) const override; | |||
| virtual void destroy_intern (const destroy_context& context) const override; | |||
| private: | |||
| virtual std::string create_update_base(const create_update_context& context) const override; | |||
| @@ -64,6 +64,36 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| : this->create_update_exec(context); | |||
| } | |||
| template<typename T_schema, typename T_table, typename T_base_dataset> | |||
| void table_polymorphic_t<T_schema, T_table, T_base_dataset> | |||
| ::destroy_intern(const destroy_context& context) const | |||
| { | |||
| bool done = false; | |||
| auto& dataset = context.get<dataset_type>(); | |||
| for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ | |||
| if (!done) | |||
| { | |||
| using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>; | |||
| auto derived_dataset_id = misc::get_type_id(hana::type_c<derived_dataset_type>); | |||
| auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); | |||
| if (!derived_table) | |||
| { | |||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
| << "unable to find derived table info for dataset '" | |||
| << utl::type_helper<derived_dataset_type>::name() << "'!").str()); | |||
| } | |||
| auto new_context = change_context(context, derived_dataset); | |||
| derived_table->destroy(new_context); | |||
| done = true; | |||
| } | |||
| }); | |||
| if (!done) | |||
| { | |||
| this->destroy_exec(context); | |||
| } | |||
| } | |||
| template<typename T_schema, typename T_table, typename T_base_dataset> | |||
| std::string table_polymorphic_t<T_schema, T_table, T_base_dataset> | |||
| ::create_update_base(const create_update_context& context) const | |||
| @@ -176,13 +176,12 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
| { | |||
| if (!known) | |||
| { | |||
| auto& ref_table = *referenced_table; | |||
| auto& key_info = *table->primary_key_field; | |||
| auto& ref_key_info = *referenced_table->primary_key_field; | |||
| auto& ref_key_info = *ref_table.primary_key_field; | |||
| std::ostringstream os; | |||
| os << "DELETE FROM `" | |||
| << ref_key_info.table_name | |||
| << "` WHERE `" | |||
| os << "WHERE `" | |||
| << ref_key_info.field_name | |||
| << "` IN (SELECT `" | |||
| << ref_key_info.table_name | |||
| @@ -205,7 +204,10 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
| << "?\?" | |||
| << ref_key_info.convert_to_close | |||
| << ")"; | |||
| known.reset(new ::cppmariadb::statement(os.str())); | |||
| auto where = os.str(); | |||
| auto query = ref_table.build_delete_query(&where); | |||
| known.reset(new ::cppmariadb::statement(query)); | |||
| } | |||
| return *known; | |||
| } | |||
| @@ -213,13 +215,12 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
| { | |||
| if (!unknown) | |||
| { | |||
| auto& ref_table = *referenced_table; | |||
| auto& key_info = *table->primary_key_field; | |||
| auto& ref_key_info = *referenced_table->primary_key_field; | |||
| auto& ref_key_info = *ref_table.primary_key_field; | |||
| std::ostringstream os; | |||
| os << "DELETE FROM `" | |||
| << ref_key_info.table_name | |||
| << "` WHERE `" | |||
| os << "WHERE `" | |||
| << ref_key_info.field_name | |||
| << "` IN (SELECT `" | |||
| << ref_key_info.table_name | |||
| @@ -234,7 +235,10 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
| << "?\?" | |||
| << key_info.convert_to_close | |||
| << ")"; | |||
| unknown.reset(new ::cppmariadb::statement(os.str())); | |||
| auto where = os.str(); | |||
| auto query = ref_table.build_delete_query(&where); | |||
| unknown.reset(new ::cppmariadb::statement(query)); | |||
| } | |||
| return *unknown; | |||
| } | |||
| @@ -443,6 +443,160 @@ struct select_query_builder_t | |||
| } | |||
| }; | |||
| /* delete_query_builder_t */ | |||
| struct delete_query_builder_t | |||
| { | |||
| const table_t& _table; | |||
| size_t alias_id { 0 }; | |||
| std::ostringstream os; | |||
| std::list<std::string> names; | |||
| delete_query_builder_t( | |||
| const table_t& p_table) | |||
| : _table (p_table) | |||
| { } | |||
| inline std::string make_alias() | |||
| { return std::string("T") + std::to_string(alias_id++); } | |||
| inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) | |||
| { | |||
| auto has_alias = !alias.empty(); | |||
| auto real_alias = has_alias ? alias : table.table_name; | |||
| if (table.base_table && add_base) | |||
| { | |||
| assert(table.base_table->primary_key_field); | |||
| auto& base_table = *table.base_table; | |||
| auto& base_key = *base_table.primary_key_field; | |||
| auto base_alias = has_alias ? make_alias() : std::string(); | |||
| auto real_base_alias = has_alias ? base_alias : base_table.table_name; | |||
| os << " LEFT JOIN `" | |||
| << base_table.table_name; | |||
| if (has_alias) | |||
| { | |||
| os << "` AS `" | |||
| << base_alias; | |||
| } | |||
| os << "` ON `" | |||
| << real_alias | |||
| << "`.`" | |||
| << base_key.field_name | |||
| << "`=`" | |||
| << real_base_alias | |||
| << "`.`" | |||
| << base_key.field_name | |||
| << "`"; | |||
| add_table(base_table, base_alias, true, false); | |||
| } | |||
| names.emplace_back(real_alias); | |||
| /* foreign table one */ | |||
| for (auto& ptr : table.foreign_table_one_fields) | |||
| { | |||
| assert(ptr); | |||
| assert(ptr->referenced_table); | |||
| assert(ptr->referenced_table->primary_key_field); | |||
| auto& field = *ptr; | |||
| auto& ref_table = *field.referenced_table; | |||
| auto& ref_key = *ref_table.primary_key_field; | |||
| auto new_alias = make_alias(); | |||
| os << " LEFT JOIN `" | |||
| << ref_table.table_name | |||
| << "` AS `" | |||
| << new_alias | |||
| << "` ON `" | |||
| << real_alias | |||
| << "`.`" | |||
| << ref_key.table_name | |||
| << "_id_" | |||
| << field.field_name | |||
| << "`=`" | |||
| << new_alias | |||
| << "`.`" | |||
| << ref_key.field_name | |||
| << "`"; | |||
| add_table(ref_table, new_alias, true, true); | |||
| } | |||
| /* derived tables */ | |||
| if (add_derived) | |||
| { | |||
| for (auto& ptr : table.derived_tables) | |||
| { | |||
| assert(ptr); | |||
| assert(ptr->primary_key_field); | |||
| auto& derived_table = *ptr; | |||
| auto& primary_key = *table.primary_key_field; | |||
| auto derived_alias = has_alias ? make_alias() : std::string(); | |||
| auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name; | |||
| os << " LEFT JOIN `" | |||
| << derived_table.table_name; | |||
| if (has_alias) | |||
| { | |||
| os << "` AS `" | |||
| << derived_alias; | |||
| } | |||
| os << "` ON `" | |||
| << real_alias | |||
| << "`.`" | |||
| << primary_key.field_name | |||
| << "`=`" | |||
| << real_derived_alias | |||
| << "`.`" | |||
| << primary_key.field_name | |||
| << "`"; | |||
| add_table(derived_table, derived_alias, false, true); | |||
| } | |||
| } | |||
| } | |||
| inline std::string operator()(const std::string* where) | |||
| { | |||
| os << " FROM `" | |||
| << _table.table_name | |||
| << "`"; | |||
| add_table(_table, "", true, true); | |||
| if (where) | |||
| { | |||
| os << " " << *where; | |||
| } | |||
| else | |||
| { | |||
| os << " ?where!"; | |||
| } | |||
| std::ostringstream ss; | |||
| ss << "DELETE "; | |||
| bool first = true; | |||
| for (auto& name : names) | |||
| { | |||
| if (first) | |||
| first = false; | |||
| else | |||
| ss << ", "; | |||
| ss << "`" | |||
| << name | |||
| << "`"; | |||
| } | |||
| ss << os.str(); | |||
| return ss.str(); | |||
| } | |||
| }; | |||
| /* build queries */ | |||
| std::string build_init_stage1_query(const table_t& table) | |||
| @@ -686,7 +840,7 @@ std::string build_init_stage2_query(const table_t& table) | |||
| << indent | |||
| << "ON DELETE CASCADE" | |||
| << indent | |||
| << "ON UPDATE NO ACTION" | |||
| << "ON UPDATE CASCADE" | |||
| << decindent; | |||
| } | |||
| @@ -723,7 +877,7 @@ std::string build_init_stage2_query(const table_t& table) | |||
| << ref_key_info.field_name | |||
| << "`)" | |||
| << indent | |||
| << "ON DELETE CASCADE" | |||
| << "ON DELETE SET NULL" | |||
| << indent | |||
| << "ON UPDATE NO ACTION" | |||
| << decindent; | |||
| @@ -1143,9 +1297,7 @@ std::string table_t::execute_create_update( | |||
| /* delete non referenced elements */ | |||
| if (context.is_update) | |||
| { | |||
| auto& delete_statement = ref_table.get_statement_foreign_many_delete(); | |||
| cpphibernate_debug_log("execute DELETE old foreign many query: " << delete_statement.query(connection)); | |||
| connection.execute(delete_statement); | |||
| ref_table.execute_foreign_many_delete(context); | |||
| } | |||
| } | |||
| @@ -1220,6 +1372,31 @@ const table_t* table_t::get_derived_by_dataset_id(size_t id) const | |||
| void table_t::emplace(const read_context& context) const | |||
| { throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); } | |||
| std::string table_t::get_where_primary_key(const data_context& context) const | |||
| { | |||
| assert(primary_key_field); | |||
| auto& field = *primary_key_field; | |||
| auto primary_key = *field.get(context); | |||
| std::ostringstream os; | |||
| os << "WHERE `" | |||
| << field.table_name | |||
| << "`.`" | |||
| << field.field_name | |||
| << "`=" | |||
| << field.convert_to_open | |||
| << "'" | |||
| << context.connection.escape(primary_key) | |||
| << "'" | |||
| << field.convert_to_close; | |||
| return os.str(); | |||
| } | |||
| std::string table_t::build_delete_query(const std::string* where) const | |||
| { return delete_query_builder_t(*this)(where); } | |||
| ::cppmariadb::statement& table_t::get_statement_create_table() const | |||
| { | |||
| if (_statement_create_table) | |||
| @@ -1282,10 +1459,8 @@ void table_t::emplace(const read_context& context) const | |||
| if (!_statement_foreign_many_delete) | |||
| { | |||
| std::ostringstream os; | |||
| os << "DELETE FROM `" | |||
| << table_name | |||
| << "` WHERE"; | |||
| auto first = true; | |||
| os << "WHERE"; | |||
| for (auto ptr : foreign_key_fields) | |||
| { | |||
| assert(ptr); | |||
| @@ -1300,11 +1475,32 @@ void table_t::emplace(const read_context& context) const | |||
| << field.field_name | |||
| << "` IS NULL)"; | |||
| } | |||
| _statement_foreign_many_delete.reset(new ::cppmariadb::statement(os.str())); | |||
| std::string where = os.str(); | |||
| auto query = delete_query_builder_t(*this)(&where); | |||
| _statement_foreign_many_delete.reset(new ::cppmariadb::statement(query)); | |||
| } | |||
| return *_statement_foreign_many_delete; | |||
| } | |||
| ::cppmariadb::statement& table_t::get_statement_delete() const | |||
| { | |||
| if (!_statement_delete) | |||
| { | |||
| auto query = delete_query_builder_t(*this)(nullptr); | |||
| _statement_delete.reset(new ::cppmariadb::statement(query)); | |||
| } | |||
| return *_statement_delete; | |||
| } | |||
| void table_t::execute_foreign_many_delete(const base_context& context) const | |||
| { | |||
| auto& connection = context.connection; | |||
| auto& statement = get_statement_foreign_many_delete(); | |||
| cpphibernate_debug_log("execute DELETE old foreign many query: " << statement.query(connection)); | |||
| connection.execute(statement); | |||
| } | |||
| std::string table_t::create_update_base(const create_update_context& context) const | |||
| { | |||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
| @@ -1328,6 +1524,9 @@ void table_t::init_stage2_exec(const init_context& context) const | |||
| connection.execute(*statement); | |||
| } | |||
| std::string table_t::create_update_intern(const create_update_context& context) const | |||
| { return create_update_exec(context); } | |||
| std::string table_t::create_update_exec(const create_update_context& context) const | |||
| { | |||
| auto& statement = context.is_update | |||
| @@ -1336,9 +1535,6 @@ std::string table_t::create_update_exec(const create_update_context& context) co | |||
| return execute_create_update(context, statement); | |||
| } | |||
| std::string table_t::create_update_intern(const create_update_context& context) const | |||
| { return create_update_exec(context); } | |||
| void table_t::read_exec(const read_context& context) const | |||
| { | |||
| auto& statement = get_statement_select(context); | |||
| @@ -1401,4 +1597,58 @@ void table_t::read_exec(const read_context& context) const | |||
| ref_table.read(next_context); | |||
| } | |||
| } | |||
| void table_t::destroy_intern(const destroy_context& context) const | |||
| { return destroy_exec(context); } | |||
| void table_t::destroy_exec(const destroy_context& context) const | |||
| { | |||
| assert(primary_key_field); | |||
| auto& connection = context.connection; | |||
| auto& statement = get_statement_delete(); | |||
| statement.set(0, context.where); | |||
| cpphibernate_debug_log("execute DELETE query: " << statement.query(connection)); | |||
| connection.execute(statement); | |||
| destroy_cleanup(context, false, true); | |||
| } | |||
| void table_t::destroy_cleanup(const base_context& context, bool check_derived, bool check_base) const | |||
| { | |||
| for (auto ptr : foreign_table_many_fields) | |||
| { | |||
| assert(ptr); | |||
| assert(ptr->referenced_table); | |||
| auto& ref_table = *ptr->referenced_table; | |||
| ref_table.execute_foreign_many_delete(context); | |||
| } | |||
| for (auto ptr : foreign_table_fields) | |||
| { | |||
| assert(ptr); | |||
| assert(ptr->referenced_table); | |||
| auto& ref_table = *ptr->referenced_table; | |||
| ref_table.destroy_cleanup(context, true, true); | |||
| } | |||
| if (check_base && base_table) | |||
| { | |||
| base_table->destroy_cleanup(context, false, true); | |||
| } | |||
| if (check_derived) | |||
| { | |||
| for (auto ptr : derived_tables) | |||
| { | |||
| assert(ptr); | |||
| ptr->destroy_cleanup(context, true, false); | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,250 @@ | |||
| #include <cpphibernate/driver/mariadb.h> | |||
| #include "test_helper.h" | |||
| #include "test_schema.h" | |||
| #include "mariadb_mock.h" | |||
| using namespace ::testing; | |||
| using namespace ::cpphibernate; | |||
| TEST(CppHibernateTests, destroy_test1) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test1` " | |||
| "FROM " | |||
| "`tbl_test1` " | |||
| "WHERE " | |||
| "`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| test1 t1; | |||
| t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(t1); | |||
| } | |||
| TEST(CppHibernateTests, destroy_test2) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2`.`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| test2 t2; | |||
| t2.id = uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(t2); | |||
| } | |||
| TEST(CppHibernateTests, destroy_test3) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test3` " | |||
| "FROM " | |||
| "`tbl_test3` " | |||
| "WHERE " | |||
| "`tbl_test3`.`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| test3 t3; | |||
| t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(t3); | |||
| } | |||
| TEST(CppHibernateTests, destroy_derived1) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_base`, " | |||
| "`tbl_derived1`, " | |||
| "`T0` " | |||
| "FROM " | |||
| "`tbl_derived1` " | |||
| "LEFT JOIN " | |||
| "`tbl_base` ON `tbl_derived1`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test1` AS `T0` ON `tbl_derived1`.`tbl_test1_id_test1_data`=`T0`.`tbl_test1_id` " | |||
| "WHERE " | |||
| "`tbl_base`.`tbl_base_id`=UuidToBin('X3d12778a-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| derived1 d1; | |||
| d1.id = uuid("3d12778a-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(static_cast<base&>(d1)); | |||
| } | |||
| TEST(CppHibernateTests, destroy_derived2) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_base`, " | |||
| "`tbl_derived2`, " | |||
| "`T0`, " | |||
| "`T1`, " | |||
| "`T2`, " | |||
| "`tbl_derived3` " | |||
| "FROM " | |||
| "`tbl_derived2` " | |||
| "LEFT JOIN " | |||
| "`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_derived3` ON `tbl_derived2`.`tbl_derived2_id`=`tbl_derived3`.`tbl_derived2_id` " | |||
| "WHERE " | |||
| "`tbl_base`.`tbl_base_id`=UuidToBin('X3d127db6-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| derived2 d2; | |||
| d2.id = uuid("3d127db6-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(static_cast<base&>(d2)); | |||
| } | |||
| TEST(CppHibernateTests, destroy_derived3) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_base`, " | |||
| "`tbl_derived2`, " | |||
| "`T0`, " | |||
| "`T1`, " | |||
| "`T2`, " | |||
| "`tbl_derived3` " | |||
| "FROM " | |||
| "`tbl_derived3` " | |||
| "LEFT JOIN " | |||
| "`tbl_derived2` ON `tbl_derived3`.`tbl_derived2_id`=`tbl_derived2`.`tbl_derived2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` " | |||
| "LEFT JOIN " | |||
| "`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` " | |||
| "WHERE " | |||
| "`tbl_derived2`.`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test3` " | |||
| "FROM " | |||
| "`tbl_test3` " | |||
| "WHERE " | |||
| "(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
| "(`tbl_derived3_id_test3_vector` IS NULL)"); | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test3` " | |||
| "FROM " | |||
| "`tbl_test3` " | |||
| "WHERE " | |||
| "(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
| "(`tbl_derived3_id_test3_vector` IS NULL)"); | |||
| expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| derived3 d3; | |||
| d3.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.destroy(static_cast<derived2&>(d3)); | |||
| } | |||
| @@ -168,11 +168,11 @@ TEST(CppHibernateTests, init) | |||
| " FOREIGN KEY (`tbl_base_id`)\n" | |||
| " REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON UPDATE NO ACTION,\n" | |||
| " ON UPDATE CASCADE,\n" | |||
| " ADD CONSTRAINT `fk_tbl_derived1_to_tbl_test1_id_test1_data`\n" | |||
| " FOREIGN KEY (`tbl_test1_id_test1_data`)\n" | |||
| " REFERENCES `test`.`tbl_test1` (`tbl_test1_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON DELETE SET NULL\n" | |||
| " ON UPDATE NO ACTION"); | |||
| expect_query(mock, "ALTER TABLE `tbl_derived2`\n" | |||
| @@ -180,21 +180,21 @@ TEST(CppHibernateTests, init) | |||
| " FOREIGN KEY (`tbl_base_id`)\n" | |||
| " REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON UPDATE NO ACTION,\n" | |||
| " ON UPDATE CASCADE,\n" | |||
| " ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_nullable`\n" | |||
| " FOREIGN KEY (`tbl_test2_id_test2_nullable`)\n" | |||
| " REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON DELETE SET NULL\n" | |||
| " ON UPDATE NO ACTION,\n" | |||
| " ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_u`\n" | |||
| " FOREIGN KEY (`tbl_test2_id_test2_ptr_u`)\n" | |||
| " REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON DELETE SET NULL\n" | |||
| " ON UPDATE NO ACTION,\n" | |||
| " ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_s`\n" | |||
| " FOREIGN KEY (`tbl_test2_id_test2_ptr_s`)\n" | |||
| " REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON DELETE SET NULL\n" | |||
| " ON UPDATE NO ACTION"); | |||
| expect_query(mock, "ALTER TABLE `tbl_derived3`\n" | |||
| @@ -202,7 +202,7 @@ TEST(CppHibernateTests, init) | |||
| " FOREIGN KEY (`tbl_derived2_id`)\n" | |||
| " REFERENCES `test`.`tbl_derived2` (`tbl_derived2_id`)\n" | |||
| " ON DELETE CASCADE\n" | |||
| " ON UPDATE NO ACTION"); | |||
| " ON UPDATE CASCADE"); | |||
| expect_query(mock, "COMMIT"); | |||
| @@ -153,7 +153,9 @@ TEST(CppHibernateTests, update_derived1) | |||
| "`u32_ptr_u`=null, " | |||
| "`u32_ptr_s`='X789X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test1` " | |||
| "FROM " | |||
| "`tbl_test1` " | |||
| "WHERE " | |||
| "`tbl_test1_id` IN (" | |||
| @@ -224,7 +226,9 @@ TEST(CppHibernateTests, update_derived2) | |||
| "WHERE " | |||
| "`tbl_test2_id`=UuidToBin('X3d1283a6-abb9-11e8-98d0-529269fb1459X')", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -249,7 +253,9 @@ TEST(CppHibernateTests, update_derived2) | |||
| "`u16_data`='X22X', " | |||
| "`i16_data`='X23X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -261,7 +267,9 @@ TEST(CppHibernateTests, update_derived2) | |||
| "`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X') AND " | |||
| "`tbl_test2_id_test2_ptr_u`!= UuidToBin('Xec0f0aac-b8b9-11e8-96f8-529269fb1459X')" | |||
| ")"); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -328,7 +336,9 @@ TEST(CppHibernateTests, update_derived3) | |||
| "WHERE " | |||
| "`tbl_base_id`=UuidToBin('X3d1288ce-abb9-11e8-98d0-529269fb1459X')", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -339,7 +349,9 @@ TEST(CppHibernateTests, update_derived3) | |||
| "WHERE " | |||
| "`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
| ")"); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -350,7 +362,9 @@ TEST(CppHibernateTests, update_derived3) | |||
| "WHERE " | |||
| "`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
| ")"); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test2` " | |||
| "FROM " | |||
| "`tbl_test2` " | |||
| "WHERE " | |||
| "`tbl_test2_id` IN (" | |||
| @@ -414,7 +428,9 @@ TEST(CppHibernateTests, update_derived3) | |||
| "`u64_data`='X112X', " | |||
| "`i64_data`='X113X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test3` " | |||
| "FROM " | |||
| "`tbl_test3` " | |||
| "WHERE " | |||
| "(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
| @@ -467,7 +483,9 @@ TEST(CppHibernateTests, update_derived3) | |||
| "WHERE " | |||
| "`tbl_test3_id`=UuidToBin('X3d129134-abb9-11e8-98d0-529269fb1459X')", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "DELETE FROM " | |||
| expect_query(mock, "DELETE " | |||
| "`tbl_test3` " | |||
| "FROM " | |||
| "`tbl_test3` " | |||
| "WHERE " | |||
| "(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
| @@ -64,8 +64,8 @@ struct result_data | |||
| inline MYSQL_RES* next_result() | |||
| { | |||
| static MYSQL_RES* value = reinterpret_cast<MYSQL_RES*>(0x1000); | |||
| return ++value; | |||
| static size_t value = 0x2000; | |||
| return reinterpret_cast<MYSQL_RES*>(value++); | |||
| } | |||
| inline const result_data::data_type& empty_result_data() | |||