diff --git a/include/cpphibernate/driver/mariadb/schema/table.h b/include/cpphibernate/driver/mariadb/schema/table.h index fe96e28..1f637fd 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.h +++ b/include/cpphibernate/driver/mariadb/schema/table.h @@ -111,9 +111,9 @@ beg_namespace_cpphibernate_driver_mariadb ::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; + ::cppmariadb::statement* get_statement_insert_into() const; ::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_update(const filter_t& filter, const field_t* owner) const; ::cppmariadb::statement& get_statement_foreign_many_delete() const; ::cppmariadb::statement& get_statement_delete() const; @@ -121,7 +121,7 @@ beg_namespace_cpphibernate_driver_mariadb std::string execute_create_update( 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; diff --git a/include/cpphibernate/schema/getter.h b/include/cpphibernate/schema/getter.h index 15924d3..d67233c 100644 --- a/include/cpphibernate/schema/getter.h +++ b/include/cpphibernate/schema/getter.h @@ -72,11 +72,11 @@ beg_namespace_cpphibernate_schema /* getter_lambda_t */ - template + template struct getter_lambda_t - : public getter_t()(std::declval()))> + : public getter_t { - using base_type = getter_t()(std::declval()))>; + using base_type = getter_t; using dataset_type = typename base_type::dataset_type; using value_type = typename base_type::value_type; using lambda_type = T_lambda; @@ -104,8 +104,8 @@ beg_namespace_cpphibernate_schema { }; template - struct is_getter_impl, T>::value>> + struct is_getter_impl, T>>> : public mp::c_true_t { }; @@ -122,19 +122,31 @@ beg_namespace_cpphibernate_schema template constexpr decltype(auto) make_getter_member_var(T_value T_dataset::* member) - { return __impl::getter_member_var_t(member); } + { + using getter_type = __impl::getter_member_var_t; + return getter_type(member); + } template constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)()) - { return __impl::getter_member_func_t(member); } + { + using getter_type = __impl::getter_member_func_t; + return getter_type(member); + } template constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)() const) - { return __impl::getter_member_func_t(member); } + { + using getter_type = __impl::getter_member_func_t; + return getter_type(member); + } - template - constexpr decltype(auto) make_getter_lambda(T_lambda&& lambda, boost::hana::basic_type) - { return __impl::getter_lambda_t(std::forward(lambda)); } + template + constexpr decltype(auto) make_getter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) + { + using getter_type = __impl::getter_lambda_t, misc::decay_unwrap_t>; + return getter_type(std::forward(lambda)); + } /* operations */ @@ -160,6 +172,11 @@ beg_namespace_cpphibernate_schema template constexpr decltype(auto) operator()(T_func&& func, hana::type&& wrapped_dataset, hana::type&& wrapped_value) const { return make_getter_lambda(std::forward(func), std::forward>(wrapped_dataset), std::forward>(wrapped_value)); } + + template + constexpr auto operator()(T_getter&& getter) const + -> mp::enable_if, T_getter> + { return std::forward(getter); } }; } diff --git a/include/cpphibernate/schema/macros.h b/include/cpphibernate/schema/macros.h index bd213d6..f3759ed 100644 --- a/include/cpphibernate/schema/macros.h +++ b/include/cpphibernate/schema/macros.h @@ -41,3 +41,18 @@ id, \ p_member_ptr, \ cpphibernate::schema::attribute::primary_key) + +#define cpphibernate_make_temp_id(p_dataset, p_value) \ + cpphibernate_make_field_custom( \ + id, \ + ::cpphibernate::schema::make_getter_lambda( \ + [](p_dataset&) { \ + return p_value { }; \ + }, \ + ::boost::hana::type_c, \ + ::boost::hana::type_c), \ + ::cpphibernate::schema::make_setter_lambda( \ + [](p_dataset&, p_value&&) { }, \ + ::boost::hana::type_c, \ + ::boost::hana::type_c), \ + cpphibernate::schema::attribute::primary_key) diff --git a/include/cpphibernate/schema/setter.h b/include/cpphibernate/schema/setter.h index 728e48a..4c2be2c 100644 --- a/include/cpphibernate/schema/setter.h +++ b/include/cpphibernate/schema/setter.h @@ -11,20 +11,21 @@ beg_namespace_cpphibernate_schema /* setter_t */ - template + template struct setter_t { - using value_type = T_value; + using dataset_type = T_dataset; + using value_type = T_value; }; - /* setter_none_t */ struct setter_none_t - : public setter_t + : public setter_t { - using base_type = setter_t; - using value_type = typename base_type::value_type; + using base_type = setter_t; + using dataset_type = typename base_type::dataset_type; + using value_type = typename base_type::value_type; cpphibernate_constructable(setter_none_t, default); cpphibernate_copyable (setter_none_t, delete); @@ -35,11 +36,11 @@ beg_namespace_cpphibernate_schema template struct setter_member_var_t - : public setter_t + : public setter_t { - using base_type = setter_t; + using base_type = setter_t; using value_type = typename base_type::value_type; - using dataset_type = T_dataset; + using dataset_type = typename base_type::dataset_type; using member_type = T_member; member_type member; @@ -61,11 +62,11 @@ beg_namespace_cpphibernate_schema template struct setter_member_func_t - : public setter_t + : public setter_t { - using base_type = setter_t; + using base_type = setter_t; using value_type = typename base_type::value_type; - using dataset_type = T_dataset; + using dataset_type = typename base_type::dataset_type; using member_type = T_member; member_type member; @@ -85,13 +86,13 @@ beg_namespace_cpphibernate_schema /* setter_lambda_t */ - template + template struct setter_lambda_t - : public setter_t + : public setter_t { - using base_type = setter_t; + using base_type = setter_t; using value_type = typename base_type::value_type; - using dataset_type = T_dataset; + using dataset_type = typename base_type::dataset_type; using lambda_type = T_lambda; lambda_type lambda; @@ -117,8 +118,8 @@ beg_namespace_cpphibernate_schema { }; template - struct is_setter_impl, T>::value>> + struct is_setter_impl, T>>> : public mp::c_true_t { }; @@ -134,23 +135,38 @@ beg_namespace_cpphibernate_schema /* make */ constexpr decltype(auto) make_setter_none() - { return __impl::setter_none_t(); } + { + using setter_type = __impl::setter_none_t; + return setter_type(); + } template constexpr decltype(auto) make_setter_member_var(T_value T_dataset::* member) - { return __impl::setter_member_var_t(member); } + { + using setter_type = __impl::setter_member_var_t; + return setter_type(member); + } template constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value)) - { return __impl::setter_member_func_t(member); } + { + using setter_type = __impl::setter_member_func_t; + return setter_type(member); + } template constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value) const) - { return __impl::setter_member_func_t(member); } + { + using setter_type = __impl::setter_member_func_t; + return setter_type(member); + } - template - constexpr decltype(auto) make_setter_lambda(T_lambda&& lambda, boost::hana::basic_type, boost::hana::basic_type) - { return __impl::setter_lambda_t(std::forward(lambda)); } + template + constexpr decltype(auto) make_setter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) + { + using setter_type = __impl::setter_lambda_t, misc::decay_unwrap_t>; + return setter_type(std::forward(lambda)); + } /* operations */ @@ -177,9 +193,14 @@ beg_namespace_cpphibernate_schema constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value&&)) const { return make_setter_member_func(member); } - template - constexpr decltype(auto) operator()(T_func&& func, hana::type&& wrapped_dataset, hana::type&& wrapped_value) const - { return make_setter_lambda(std::forward(func), std::forward>(wrapped_dataset), std::forward>(wrapped_value)); } + template + constexpr decltype(auto) operator()(T_func&& func, T_wrapped_dataset&&, T_wrapped_value&&) const + { return make_setter_lambda(std::forward(func), T_wrapped_dataset { }, T_wrapped_value { }); } + + template + constexpr auto operator()(T_setter&& setter) const + -> mp::enable_if, T_setter> + { return std::forward(setter); } }; } diff --git a/src/cpphibernate/driver/mariadb/schema/table.cpp b/src/cpphibernate/driver/mariadb/schema/table.cpp index 6e2d9f3..cd95f2f 100644 --- a/src/cpphibernate/driver/mariadb/schema/table.cpp +++ b/src/cpphibernate/driver/mariadb/schema/table.cpp @@ -1088,7 +1088,9 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt << key_info.convert_to_close; } - return os.str(); + return index == 0 + ? std::string() + : os.str(); } std::string build_select_query( @@ -1103,7 +1105,7 @@ std::string build_select_query( std::string table_t::execute_create_update( const create_update_context& context, - ::cppmariadb::statement& statement) const + ::cppmariadb::statement* statement) const { auto& connection = context.connection; auto& filter = context.filter; @@ -1112,7 +1114,7 @@ std::string table_t::execute_create_update( bool is_update = context.is_update; std::string primary_key; - statement.clear(); + if (statement) statement->clear(); /* primary key */ assert(primary_key_field); @@ -1120,7 +1122,7 @@ std::string table_t::execute_create_update( && !is_update) { primary_key = primary_key_field->generate_value(context.connection); - statement.set(index, primary_key); + if (statement) statement->set(index, primary_key); ++index; } else @@ -1137,7 +1139,7 @@ std::string table_t::execute_create_update( if (!new_context.derived_table) new_context.derived_table = this; std::string key = create_update_base(new_context); - statement.set(index, std::move(key)); + if (statement) statement->set(index, std::move(key)); ++index; } @@ -1155,11 +1157,17 @@ std::string table_t::execute_create_update( /* insert/update dataset */ value_t key = field.foreign_create_update(context); if (key.has_value()) - statement.set(index, std::move(key)); + { + if (statement) statement->set(index, std::move(key)); + } else if (field.value_is_nullable) - statement.set_null(index); + { + if (statement) statement->set_null(index); + } else + { throw misc::hibernate_exception("Received null key for non nullable foreign dataset!"); + } ++index; /* cleanup old dataset (if new one was created) */ @@ -1189,18 +1197,24 @@ std::string table_t::execute_create_update( if (set_value) { assert(!context.owner_key.empty()); - statement.set(index, context.owner_key); + if (statement) statement->set(index, context.owner_key); } else { - statement.set_null(index); + if (statement) statement->set_null(index); } ++index; if (field_info.value_is_ordered) { - if (set_value) statement.set(index, context.index); - else statement.set(index, 0); + if (set_value) + { + if (statement) statement->set(index, context.index); + } + else + { + if (statement) statement->set(index, 0); + } ++index; } } @@ -1215,8 +1229,14 @@ std::string table_t::execute_create_update( auto& field_info = *ptr; auto value = field_info.get(context); - if (value.has_value()) statement.set(index, *value); - else statement.set_null(index); + if (value.has_value()) + { + if (statement) statement->set(index, *value); + } + else + { + if (statement) statement->set_null(index); + } ++index; } @@ -1225,7 +1245,7 @@ std::string table_t::execute_create_update( && !base_table && !is_update) { - statement.set(index, context.derived_table + if (statement) statement->set(index, context.derived_table ? context.derived_table->table_id : table_id); ++index; @@ -1235,34 +1255,37 @@ std::string table_t::execute_create_update( if (is_update) { assert(primary_key_field); - statement.set(index, *primary_key_field->get(context)); + if (statement) statement->set(index, *primary_key_field->get(context)); ++index; } /* execute */ - if (!is_update) - { - cpphibernate_debug_log("execute INSERT query: " << statement.query(connection)); - } - else + if (statement) { - cpphibernate_debug_log("execute UPDATE query: " << statement.query(connection)); - } + if (!is_update) + { + cpphibernate_debug_log("execute INSERT query: " << statement->query(connection)); + } + else + { + cpphibernate_debug_log("execute UPDATE query: " << statement->query(connection)); + } - if ( primary_key_field->value_is_auto_incremented - && !is_update) - { - auto id = connection.execute_id(statement); - primary_key = utl::to_string(id); - } - else - { - auto count = connection.execute_rows(statement); - if (count != 1) - throw misc::hibernate_exception("Expected exaclty one row to be inserted/updated!"); - cpphibernate_debug_log(count << " rows inserted/updated"); + if ( primary_key_field->value_is_auto_incremented + && !is_update) + { + auto id = connection.execute_id(*statement); + primary_key = utl::to_string(id); + } + else + { + auto count = connection.execute_rows(*statement); + if (count != 1) + throw misc::hibernate_exception("Expected exaclty one row to be inserted/updated!"); + cpphibernate_debug_log(count << " rows inserted/updated"); + } + primary_key_field->set(context, primary_key); } - primary_key_field->set(context, primary_key); /* foreign table many fields */ for (auto& ptr : foreign_table_many_fields) @@ -1437,18 +1460,21 @@ std::string table_t::build_delete_query(const std::string* where) const auto query = build_init_stage2_query(*this); _statement_alter_table.reset(new ::cppmariadb::statement(query)); } - if (_statement_alter_table->empty()) - return nullptr; - return _statement_alter_table.get(); + return _statement_alter_table->empty() + ? nullptr + : _statement_alter_table.get(); } -::cppmariadb::statement& table_t::get_statement_insert_into() const +::cppmariadb::statement* table_t::get_statement_insert_into() const { - if (_statement_insert_into) - return *_statement_insert_into; - auto query = build_create_update_query(*this, nullptr, nullptr); - _statement_create_table.reset(new ::cppmariadb::statement(query)); - return *_statement_create_table; + if (!_statement_insert_into) + { + auto query = build_create_update_query(*this, nullptr, nullptr); + _statement_insert_into.reset(new ::cppmariadb::statement(query)); + } + return _statement_insert_into->empty() + ? nullptr + : _statement_insert_into.get(); } ::cppmariadb::statement& table_t::get_statement_select(const read_context& context) const @@ -1466,7 +1492,7 @@ std::string table_t::build_delete_query(const std::string* where) const return it->second; } -::cppmariadb::statement& table_t::get_statement_update(const filter_t& filter, const field_t* owner) const +::cppmariadb::statement* table_t::get_statement_update(const filter_t& filter, const field_t* owner) const { auto key = std::make_tuple(filter.cache_id, owner); auto it = _statement_update.find(key); @@ -1475,7 +1501,9 @@ std::string table_t::build_delete_query(const std::string* where) const auto query = build_create_update_query(*this, &filter, owner); it = _statement_update.emplace(key, ::cppmariadb::statement(query)).first; } - return it->second; + return it->second.empty() + ? nullptr + : &it->second; } ::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const @@ -1586,7 +1614,7 @@ std::string table_t::create_update_intern(const create_update_context& context) std::string table_t::create_update_exec(const create_update_context& context) const { - auto& statement = context.is_update + auto* statement = context.is_update ? get_statement_update(context.filter, context.owner_field) : get_statement_insert_into(); return execute_create_update(context, statement); diff --git a/test/cpphibernate_create.cpp b/test/cpphibernate_create.cpp index a100dca..b2b0517 100644 --- a/test/cpphibernate_create.cpp +++ b/test/cpphibernate_create.cpp @@ -449,3 +449,72 @@ TEST(CppHibernateTests, create_derived3) auto context = make_context(test_schema, connection); context.create(static_cast(d3)); } + +TEST(CppHibernateTests, create_dummy_owner) +{ + StrictMock mock; + + expect_query(mock, "START TRANSACTION"); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0000-000000000001" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_owner` " + "SET " + "`tbl_dummy_owner_id`=UuidToBin('X00000000-0000-0000-0000-000000000001X')", + result_affected_rows(1)); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000001" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000001X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X0X', " + "`data`='X123X'", + result_affected_rows(1)); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000002" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000002X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X1X', " + "`data`='X456X'", + result_affected_rows(1)); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000003" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000003X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X2X', " + "`data`='X789X'", + 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))); + + dummy_owner d; + d.dummies.emplace_back(123); + d.dummies.emplace_back(456); + d.dummies.emplace_back(789); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.create(d); +} \ No newline at end of file diff --git a/test/cpphibernate_init.cpp b/test/cpphibernate_init.cpp index 1e36dbb..666f430 100644 --- a/test/cpphibernate_init.cpp +++ b/test/cpphibernate_init.cpp @@ -151,6 +151,28 @@ TEST(CppHibernateTests, init) "ENGINE = InnoDB\n" "DEFAULT CHARACTER SET = utf8"); + expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_dummy_id`\n" + "(\n" + " `tbl_dummy_id_id` BINARY(16) NOT NULL,\n" + " `tbl_dummy_owner_id_dummies` BINARY(16) NULL DEFAULT NULL,\n" + " `tbl_dummy_owner_index_dummies` INT UNSIGNED NOT NULL,\n" + " `data` INT NOT NULL,\n" + " PRIMARY KEY ( `tbl_dummy_id_id` ),\n" + " UNIQUE INDEX `index_tbl_dummy_id_id` ( `tbl_dummy_id_id` ASC ),\n" + " INDEX `index_tbl_dummy_owner_id_dummies` ( `tbl_dummy_owner_id_dummies` ASC )\n" + ")\n" + "ENGINE = InnoDB\n" + "DEFAULT CHARACTER SET = utf8"); + + expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_dummy_owner`\n" + "(\n" + " `tbl_dummy_owner_id` BINARY(16) NOT NULL,\n" + " PRIMARY KEY ( `tbl_dummy_owner_id` ),\n" + " UNIQUE INDEX `index_tbl_dummy_owner_id` ( `tbl_dummy_owner_id` ASC )\n" + ")\n" + "ENGINE = InnoDB\n" + "DEFAULT CHARACTER SET = utf8"); + expect_query(mock, "ALTER TABLE `tbl_test3`\n" " ADD CONSTRAINT `fk_tbl_test3_to_tbl_derived3_id_test3_list`\n" " FOREIGN KEY IF NOT EXISTS (`tbl_derived3_id_test3_list`)\n" @@ -204,6 +226,13 @@ TEST(CppHibernateTests, init) " ON DELETE CASCADE\n" " ON UPDATE CASCADE"); + expect_query(mock, "ALTER TABLE `tbl_dummy_id`\n" + " ADD CONSTRAINT `fk_tbl_dummy_id_to_tbl_dummy_owner_id_dummies`\n" + " FOREIGN KEY IF NOT EXISTS (`tbl_dummy_owner_id_dummies`)\n" + " REFERENCES `test`.`tbl_dummy_owner` (`tbl_dummy_owner_id`)\n" + " ON DELETE SET NULL\n" + " ON UPDATE NO ACTION"); + expect_query(mock, "COMMIT"); EXPECT_CALL( diff --git a/test/cpphibernate_read.cpp b/test/cpphibernate_read.cpp index 4e5a55a..ef100d3 100644 --- a/test/cpphibernate_read.cpp +++ b/test/cpphibernate_read.cpp @@ -863,4 +863,58 @@ TEST(CppHibernateTests, read_base_ptr_vector_dynamic) } EXPECT_EQ(bIt, base_vec.end()); +} + +TEST(CppHibernateTests, read_dummy_owner) +{ + StrictMock mock; + + expect_query(mock, "START TRANSACTION"); + expect_query(mock, "SELECT " + "BinToUuid(`tbl_dummy_owner`.`tbl_dummy_owner_id`) " + "FROM " + "`tbl_dummy_owner` " + "WHERE " + "(`tbl_dummy_owner`.`tbl_dummy_owner_id`=UuidToBin('X00000000-0000-0000-0000-000000000001X')) ", + result_used({ + { "00000000-0000-0000-0000-000000000001" }, + })); + expect_query(mock, "SELECT " + "BinToUuid(`tbl_dummy_id`.`tbl_dummy_id_id`), " + "`tbl_dummy_id`.`data` " + "FROM " + "`tbl_dummy_id` WHERE (`tbl_dummy_id`.`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X')) " + "ORDER BY " + "`tbl_dummy_id`.`tbl_dummy_owner_index_dummies` ASC ", + result_used({ + { "00000000-0000-0000-0001-000000000001", "123" }, + { "00000000-0000-0000-0001-000000000002", "456" }, + { "00000000-0000-0000-0001-000000000003", "789" }, + })); + 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())); + + dummy_owner d; + d.id = uuid("00000000-0000-0000-0000-000000000001"); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + + context.read(d); + + EXPECT_EQ(d.id, uuid("00000000-0000-0000-0000-000000000001")); + ASSERT_EQ(d.dummies.size(), 3); + EXPECT_EQ(d.dummies[0].data, 123); + EXPECT_EQ(d.dummies[1].data, 456); + EXPECT_EQ(d.dummies[2].data, 789); } \ No newline at end of file diff --git a/test/cpphibernate_update.cpp b/test/cpphibernate_update.cpp index a4316e6..7ba371c 100644 --- a/test/cpphibernate_update.cpp +++ b/test/cpphibernate_update.cpp @@ -634,4 +634,79 @@ TEST(CppHibernateTests, update_dynamic_base) ::cppmariadb::connection connection(reinterpret_cast(0x1111)); auto context = make_context(test_schema, connection); context.update(b); +} + +TEST(CppHibernateTests, update_dummy_owner) +{ + StrictMock mock; + + expect_query(mock, "START TRANSACTION"); + expect_query(mock, "UPDATE " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_owner_id_dummies`=NULL, " + "`tbl_dummy_owner_index_dummies`=0 " + "WHERE " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X')"); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000001" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000001X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X0X', " + "`data`='X123X'", + result_affected_rows(1)); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000002" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000002X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X1X', " + "`data`='X456X'", + result_affected_rows(1)); + expect_query(mock, "SELECT Uuid()", result_used({ + { "00000000-0000-0000-0001-000000000003" } + })); + expect_query(mock, "INSERT INTO " + "`tbl_dummy_id` " + "SET " + "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000003X'), " + "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " + "`tbl_dummy_owner_index_dummies`='X2X', " + "`data`='X789X'", + result_affected_rows(1)); + expect_query(mock, "DELETE " + "`tbl_dummy_id` " + "FROM " + "`tbl_dummy_id` " + "WHERE " + "(`tbl_dummy_owner_id_dummies` 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))); + + dummy_owner d; + d.id = uuid("00000000-0000-0000-0000-000000000001"); + d.dummies.emplace_back(123); + d.dummies.emplace_back(456); + d.dummies.emplace_back(789); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + context.update(d); } \ No newline at end of file diff --git a/test/test_schema.h b/test/test_schema.h index c71de21..253d148 100644 --- a/test/test_schema.h +++ b/test/test_schema.h @@ -93,6 +93,21 @@ struct derived3 std::vector test3_vector; }; +struct dummy_id +{ + int data; + + inline dummy_id(int p_data = 0) + : data(p_data) + { } +}; + +struct dummy_owner +{ + ::cpphibernate::uuid id; + std::vector dummies; +}; + constexpr decltype(auto) test_schema = cpphibernate_make_schema( test, cpphibernate_make_table_name( @@ -158,5 +173,19 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( cpphibernate_make_id (&derived3::derived3_id), cpphibernate_make_field (derived3, test3_list), cpphibernate_make_field (derived3, test3_vector) + ), + cpphibernate_make_table_name( + tbl_dummy_id, + dummy_id, + 14, + cpphibernate_make_temp_id (dummy_id, ::cpphibernate::uuid), + cpphibernate_make_field (dummy_id, data) + ), + cpphibernate_make_table_name( + tbl_dummy_owner, + dummy_owner, + 15, + cpphibernate_make_id (&dummy_owner::id), + cpphibernate_make_field (dummy_owner, dummies) ) ); \ No newline at end of file