| @@ -98,6 +98,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| using map_key = std::tuple<size_t, const field_t*>; | using map_key = std::tuple<size_t, const field_t*>; | ||||
| using statement_map = std::map<map_key, ::cppmariadb::statement>; | using statement_map = std::map<map_key, ::cppmariadb::statement>; | ||||
| mutable statement_ptr _statement_key_from_base; | |||||
| mutable statement_ptr _statement_create_table; | mutable statement_ptr _statement_create_table; | ||||
| mutable statement_ptr _statement_alter_table; | mutable statement_ptr _statement_alter_table; | ||||
| mutable statement_ptr _statement_insert_into; | 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_foreign_many_delete; | ||||
| mutable statement_ptr _statement_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_create_table() const; | ||||
| ::cppmariadb::statement* get_statement_alter_table() const; | ::cppmariadb::statement* get_statement_alter_table() const; | ||||
| ::cppmariadb::statement& get_statement_insert_into() const; | ::cppmariadb::statement& get_statement_insert_into() const; | ||||
| @@ -121,6 +123,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| const create_update_context& context, | const create_update_context& context, | ||||
| ::cppmariadb::statement& statement) const; | ::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; | virtual std::string create_update_base(const create_update_context& context) const; | ||||
| protected: | protected: | ||||
| @@ -1125,7 +1125,7 @@ std::string table_t::execute_create_update( | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| primary_key = *primary_key_field->get(context); | |||||
| primary_key = get_primary_key(context); | |||||
| } | } | ||||
| /* base_key */ | /* 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 | std::string table_t::build_delete_query(const std::string* where) const | ||||
| { return delete_query_builder_t(*this)(where); } | { 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 | ::cppmariadb::statement& table_t::get_statement_create_table() const | ||||
| { | { | ||||
| if (_statement_create_table) | if (_statement_create_table) | ||||
| @@ -1501,6 +1529,37 @@ void table_t::execute_foreign_many_delete(const base_context& context) const | |||||
| connection.execute(statement); | 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>(); | |||||
| } | |||||
| std::string table_t::create_update_base(const create_update_context& context) const | std::string table_t::create_update_base(const create_update_context& context) const | ||||
| { | { | ||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | ||||
| @@ -26,5 +26,6 @@ If ( __COTIRE_INCLUDED ) | |||||
| Cotire ( test_cpphibernate ) | Cotire ( test_cpphibernate ) | ||||
| EndIf ( ) | EndIf ( ) | ||||
| If ( __CMAKE_TESTS_INCLUDED ) | If ( __CMAKE_TESTS_INCLUDED ) | ||||
| Add_CMake_Test ( cpphibernate test_cpphibernate ) | |||||
| Add_CMake_Test ( NAME cpphibernate | |||||
| TARGET test_cpphibernate ) | |||||
| EndIf ( ) | EndIf ( ) | ||||
| @@ -540,4 +540,98 @@ TEST(CppHibernateTests, update_derived3) | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| context.update(static_cast<derived2&>(d3)); | context.update(static_cast<derived2&>(d3)); | ||||
| } | |||||
| TEST(CppHibernateTests, update_dynamic_base) | |||||
| { | |||||
| StrictMock<mariadb_mock> 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<MYSQL*>(0x1111), _, _, _)) | |||||
| .Times(AnyNumber()) | |||||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_close( | |||||
| reinterpret_cast<MYSQL*>(0x1111))); | |||||
| std::unique_ptr<base> b; | |||||
| b.reset(new derived2()); | |||||
| auto& d2 = *dynamic_cast<derived2*>(b.get()); | |||||
| d2.id = uuid("f9f13c08-c6e2-11e8-a8d5-f2801f1b9fd1"); | |||||
| d2.name = "dynamic derived 1"; | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.update(b); | |||||
| } | } | ||||