diff --git a/include/cpphibernate/driver/mariadb/schema/table.h b/include/cpphibernate/driver/mariadb/schema/table.h index 6f720b8..fe96e28 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.h +++ b/include/cpphibernate/driver/mariadb/schema/table.h @@ -98,6 +98,7 @@ beg_namespace_cpphibernate_driver_mariadb using map_key = std::tuple; using statement_map = std::map; + mutable statement_ptr _statement_key_from_base; mutable statement_ptr _statement_create_table; mutable statement_ptr _statement_alter_table; mutable statement_ptr _statement_insert_into; @@ -107,6 +108,7 @@ beg_namespace_cpphibernate_driver_mariadb mutable statement_ptr _statement_foreign_many_delete; mutable statement_ptr _statement_delete; + ::cppmariadb::statement& get_statement_key_from_base() const; ::cppmariadb::statement& get_statement_create_table() const; ::cppmariadb::statement* get_statement_alter_table() const; ::cppmariadb::statement& get_statement_insert_into() const; @@ -121,6 +123,9 @@ beg_namespace_cpphibernate_driver_mariadb const create_update_context& context, ::cppmariadb::statement& statement) const; + std::string get_primary_key(const data_context& context) const; + std::string get_key_from_base(const data_context& context) const; + virtual std::string create_update_base(const create_update_context& context) const; protected: diff --git a/src/cpphibernate/driver/mariadb/schema/table.cpp b/src/cpphibernate/driver/mariadb/schema/table.cpp index ce02b85..3cb1797 100644 --- a/src/cpphibernate/driver/mariadb/schema/table.cpp +++ b/src/cpphibernate/driver/mariadb/schema/table.cpp @@ -1125,7 +1125,7 @@ std::string table_t::execute_create_update( } else { - primary_key = *primary_key_field->get(context); + primary_key = get_primary_key(context); } /* base_key */ @@ -1397,6 +1397,34 @@ std::string table_t::get_where_primary_key(const data_context& context) const std::string table_t::build_delete_query(const std::string* where) const { return delete_query_builder_t(*this)(where); } +::cppmariadb::statement& table_t::get_statement_key_from_base() const +{ + if (!_statement_key_from_base) + { + if (!base_table) + throw exception(std::string("table has no base table: ") + table_name); + assert(primary_key_field); + assert(base_table); + assert(base_table->primary_key_field); + auto& key_info = *primary_key_field; + auto& base_key = *base_table->primary_key_field; + std::ostringstream os; + os << "SELECT `" + << key_info.table_name + << "`.`" + << key_info.field_name + << "` FROM `" + << key_info.table_name + << "` WHERE `" + << key_info.table_name + << "`.`" + << base_key.table_name + << "_id`=?\?"; + _statement_key_from_base.reset(new ::cppmariadb::statement(os.str())); + } + return *_statement_key_from_base; +} + ::cppmariadb::statement& table_t::get_statement_create_table() const { if (_statement_create_table) @@ -1501,6 +1529,37 @@ void table_t::execute_foreign_many_delete(const base_context& context) const connection.execute(statement); } +std::string table_t::get_primary_key(const data_context& context) const +{ + assert(primary_key_field); + if (primary_key_field->is_default(context)) + { + auto key = get_key_from_base(context); + primary_key_field->set(context, key); + return key; + } + else + { + return *primary_key_field->get(context); + } +} + +std::string table_t::get_key_from_base(const data_context& context) const +{ + if (!base_table) + throw exception(std::string("table has no base table: ") + table_name); + auto& statement = get_statement_key_from_base(); + auto base_key = base_table->get_primary_key(context); + statement.set(0, base_key); + auto result = context.connection.execute_stored(statement); + if (!result) + throw exception("unable to fetch key from database: unable to execute query!"); + auto row = result->next(); + if (!row) + throw exception("unable to fetch key from database: result set is empty!"); + return row->at(0).get(); +} + std::string table_t::create_update_base(const create_update_context& context) const { throw misc::hibernate_exception(static_cast(std::ostringstream { } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ccfdf24..73e13ac 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,5 +26,6 @@ If ( __COTIRE_INCLUDED ) Cotire ( test_cpphibernate ) EndIf ( ) If ( __CMAKE_TESTS_INCLUDED ) - Add_CMake_Test ( cpphibernate test_cpphibernate ) + Add_CMake_Test ( NAME cpphibernate + TARGET test_cpphibernate ) EndIf ( ) diff --git a/test/cpphibernate_update.cpp b/test/cpphibernate_update.cpp index 7cc5081..594aa28 100644 --- a/test/cpphibernate_update.cpp +++ b/test/cpphibernate_update.cpp @@ -540,4 +540,98 @@ TEST(CppHibernateTests, update_derived3) ::cppmariadb::connection connection(reinterpret_cast(0x1111)); auto context = make_context(test_schema, connection); context.update(static_cast(d3)); +} + +TEST(CppHibernateTests, update_dynamic_base) +{ + StrictMock mock; + + expect_query(mock, "START TRANSACTION"); + expect_query(mock, "SELECT " + "`tbl_derived2`.`tbl_derived2_id` " + "FROM " + "`tbl_derived2` " + "WHERE " + "`tbl_derived2`.`tbl_base_id`='Xf9f13c08-c6e2-11e8-a8d5-f2801f1b9fd1X'", + result_stored({ + { "ae0e7888-c6e6-11e8-a8d5-f2801f1b9fd1" } + })); + expect_query(mock, "UPDATE " + "`tbl_base` " + "SET " + "`name`='Xdynamic derived 1X' " + "WHERE " + "`tbl_base_id`=UuidToBin('Xf9f13c08-c6e2-11e8-a8d5-f2801f1b9fd1X')", + result_affected_rows(1)); + expect_query(mock, "DELETE " + "`tbl_test2` " + "FROM " + "`tbl_test2` " + "WHERE " + "`tbl_test2_id` IN (" + "SELECT " + "`tbl_test2_id_test2_nullable` " + "FROM " + "`tbl_derived2` " + "WHERE " + "`tbl_derived2_id`=UuidToBin('Xae0e7888-c6e6-11e8-a8d5-f2801f1b9fd1X')" + ")"); + expect_query(mock, "DELETE " + "`tbl_test2` " + "FROM " + "`tbl_test2` " + "WHERE " + "`tbl_test2_id` IN (" + "SELECT " + "`tbl_test2_id_test2_ptr_u` " + "FROM " + "`tbl_derived2` " + "WHERE " + "`tbl_derived2_id`=UuidToBin('Xae0e7888-c6e6-11e8-a8d5-f2801f1b9fd1X')" + ")"); + expect_query(mock, "DELETE " + "`tbl_test2` " + "FROM " + "`tbl_test2` " + "WHERE " + "`tbl_test2_id` IN (" + "SELECT " + "`tbl_test2_id_test2_ptr_s` " + "FROM " + "`tbl_derived2` " + "WHERE " + "`tbl_derived2_id`=UuidToBin('Xae0e7888-c6e6-11e8-a8d5-f2801f1b9fd1X')" + ")"); + expect_query(mock, "UPDATE " + "`tbl_derived2` " + "SET " + "`tbl_base_id`=UuidToBin('Xf9f13c08-c6e2-11e8-a8d5-f2801f1b9fd1X'), " + "`tbl_test2_id_test2_nullable`=UuidToBin(null), " + "`tbl_test2_id_test2_ptr_u`=UuidToBin(null), " + "`tbl_test2_id_test2_ptr_s`=UuidToBin(null) " + "WHERE " + "`tbl_derived2_id`=UuidToBin('Xae0e7888-c6e6-11e8-a8d5-f2801f1b9fd1X')", + result_affected_rows(1)); + 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))); + + std::unique_ptr b; + b.reset(new derived2()); + auto& d2 = *dynamic_cast(b.get()); + d2.id = uuid("f9f13c08-c6e2-11e8-a8d5-f2801f1b9fd1"); + d2.name = "dynamic derived 1"; + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.update(b); } \ No newline at end of file