From 981f6eea2e4ae15626f9965306c2e7097e349639 Mon Sep 17 00:00:00 2001 From: bergmann Date: Sat, 15 Sep 2018 14:00:48 +0200 Subject: [PATCH] * implemented destroy method for mariadb driver --- .../driver/mariadb/helper/context.h | 19 ++ include/cpphibernate/driver/mariadb/impl.h | 1 + .../driver/mariadb/impl/destroy.h | 96 ++++++ include/cpphibernate/driver/mariadb/mariadb.h | 15 + .../driver/mariadb/schema/table.h | 16 +- .../driver/mariadb/schema/table.inl | 30 ++ src/driver/mariadb/schema/field.cpp | 24 +- src/driver/mariadb/schema/table.cpp | 274 +++++++++++++++++- test/cpphibernate_destroy.cpp | 250 ++++++++++++++++ test/cpphibernate_init.cpp | 14 +- test/cpphibernate_update.cpp | 36 ++- test/test_helper.h | 4 +- 12 files changed, 738 insertions(+), 41 deletions(-) create mode 100644 include/cpphibernate/driver/mariadb/impl/destroy.h create mode 100644 test/cpphibernate_destroy.cpp diff --git a/include/cpphibernate/driver/mariadb/helper/context.h b/include/cpphibernate/driver/mariadb/helper/context.h index 0e1a536..1efb68a 100644 --- a/include/cpphibernate/driver/mariadb/helper/context.h +++ b/include/cpphibernate/driver/mariadb/helper/context.h @@ -195,5 +195,24 @@ beg_namespace_cpphibernate_driver_mariadb 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) + { } + }; + } end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/impl.h b/include/cpphibernate/driver/mariadb/impl.h index 611d188..4843de5 100644 --- a/include/cpphibernate/driver/mariadb/impl.h +++ b/include/cpphibernate/driver/mariadb/impl.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include diff --git a/include/cpphibernate/driver/mariadb/impl/destroy.h b/include/cpphibernate/driver/mariadb/impl/destroy.h new file mode 100644 index 0000000..add7eea --- /dev/null +++ b/include/cpphibernate/driver/mariadb/impl/destroy.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include +#include +#include +#include + +beg_namespace_cpphibernate_driver_mariadb +{ + + /* destroy_impl_t */ + + 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& 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 + struct destroy_impl_t< + T_dataset, + mp::enable_if>> + { + using dataset_type = T_dataset; + using nullable_helper_type = misc::nullable_helper; + + static inline void apply(const destroy_context& context, bool strict = true) + { + auto& dataset = context.get(); + auto* value = nullable_helper_type::get(dataset); + + if (value) + { + using new_dataset_type = mp::decay_t; + using new_destroy_impl_type = destroy_impl_t; + 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 + struct destroy_impl_t< + T_dataset, + mp::enable_if>> + { + using dataset_type = T_dataset; + + static inline void apply(const destroy_context& context, bool strict = true) + { + auto& connection = context.connection; + auto& dataset = context.get(); + + transaction_lock trans(connection); + for (auto& x : dataset) + { + using new_dataset_type = mp::decay_t; + using new_destroy_impl_type = destroy_impl_t; + auto new_context = change_context(context, x); + new_destroy_impl_type::apply(new_context, strict); + } + trans.commit(); + } + }; + +} +end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/mariadb.h b/include/cpphibernate/driver/mariadb/mariadb.h index fe7dfa6..e2dcbcc 100644 --- a/include/cpphibernate/driver/mariadb/mariadb.h +++ b/include/cpphibernate/driver/mariadb/mariadb.h @@ -65,6 +65,21 @@ beg_namespace_cpphibernate_driver_mariadb 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); + } }; } diff --git a/include/cpphibernate/driver/mariadb/schema/table.h b/include/cpphibernate/driver/mariadb/schema/table.h index b3d29df..6f720b8 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.h +++ b/include/cpphibernate/driver/mariadb/schema/table.h @@ -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 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; diff --git a/include/cpphibernate/driver/mariadb/schema/table.inl b/include/cpphibernate/driver/mariadb/schema/table.inl index fe990d2..f569b4a 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.inl +++ b/include/cpphibernate/driver/mariadb/schema/table.inl @@ -64,6 +64,36 @@ beg_namespace_cpphibernate_driver_mariadb : this->create_update_exec(context); } + template + void table_polymorphic_t + ::destroy_intern(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 = misc::get_type_id(hana::type_c); + auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); + if (!derived_table) + { + throw misc::hibernate_exception(static_cast(std::ostringstream { } + << "unable to find derived table info for dataset '" + << utl::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 std::string table_polymorphic_t ::create_update_base(const create_update_context& context) const diff --git a/src/driver/mariadb/schema/field.cpp b/src/driver/mariadb/schema/field.cpp index 95f9f4b..974e8c4 100644 --- a/src/driver/mariadb/schema/field.cpp +++ b/src/driver/mariadb/schema/field.cpp @@ -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; } diff --git a/src/driver/mariadb/schema/table.cpp b/src/driver/mariadb/schema/table.cpp index 01ee580..78a7246 100644 --- a/src/driver/mariadb/schema/table.cpp +++ b/src/driver/mariadb/schema/table.cpp @@ -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 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 { } @@ -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); + } + } } \ No newline at end of file diff --git a/test/cpphibernate_destroy.cpp b/test/cpphibernate_destroy.cpp new file mode 100644 index 0000000..58aaf4d --- /dev/null +++ b/test/cpphibernate_destroy.cpp @@ -0,0 +1,250 @@ +#include + +#include "test_helper.h" +#include "test_schema.h" +#include "mariadb_mock.h" + +using namespace ::testing; +using namespace ::cpphibernate; + +TEST(CppHibernateTests, destroy_test1) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + test1 t1; + t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(t1); +} + +TEST(CppHibernateTests, destroy_test2) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + test2 t2; + t2.id = uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(t2); +} + +TEST(CppHibernateTests, destroy_test3) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + test3 t3; + t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(t3); +} + +TEST(CppHibernateTests, destroy_derived1) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + derived1 d1; + d1.id = uuid("3d12778a-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(static_cast(d1)); +} + +TEST(CppHibernateTests, destroy_derived2) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + derived2 d2; + d2.id = uuid("3d127db6-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(static_cast(d2)); +} + +TEST(CppHibernateTests, destroy_derived3) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + derived3 d3; + d3.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.destroy(static_cast(d3)); +} \ No newline at end of file diff --git a/test/cpphibernate_init.cpp b/test/cpphibernate_init.cpp index ca641f2..ee892ab 100644 --- a/test/cpphibernate_init.cpp +++ b/test/cpphibernate_init.cpp @@ -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"); diff --git a/test/cpphibernate_update.cpp b/test/cpphibernate_update.cpp index 028b1ec..7cc5081 100644 --- a/test/cpphibernate_update.cpp +++ b/test/cpphibernate_update.cpp @@ -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 " diff --git a/test/test_helper.h b/test/test_helper.h index 420b2c2..9c50e7b 100644 --- a/test/test_helper.h +++ b/test/test_helper.h @@ -64,8 +64,8 @@ struct result_data inline MYSQL_RES* next_result() { - static MYSQL_RES* value = reinterpret_cast(0x1000); - return ++value; + static size_t value = 0x2000; + return reinterpret_cast(value++); } inline const result_data::data_type& empty_result_data()