@@ -32,7 +32,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
if (!strict) | |||
{ | |||
auto update_context = context; | |||
update_context.is_update = true; | |||
update_context.is_update = !update_context.is_update; | |||
ret = table.create_update(update_context); | |||
} | |||
else if (context.is_update) | |||
@@ -12,6 +12,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
namespace __impl | |||
{ | |||
/* declaration */ | |||
template<typename T, typename = void> | |||
struct make_read_context_impl | |||
{ | |||
@@ -20,6 +22,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
{ static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); } | |||
}; | |||
/* normal datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
@@ -69,6 +73,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
{ return context_impl(dataset, std::forward<T_args>(args)...); } | |||
}; | |||
/* nullable datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
@@ -131,6 +137,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||
{ return context_impl(dataset, std::forward<T_args>(args)...); } | |||
}; | |||
/* container datasets */ | |||
template<typename T_dataset, typename... T_args> | |||
struct make_read_context_impl< | |||
mp::list<T_dataset, T_args...>, | |||
@@ -58,6 +58,13 @@ beg_namespace_cpphibernate_driver_mariadb | |||
table.read(context); | |||
trans.commit(); | |||
} | |||
template<typename T_dataset> | |||
inline void update_impl(T_dataset& dataset) const | |||
{ | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, _connection, _filter, true)); | |||
} | |||
}; | |||
} |
@@ -17,6 +17,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
struct field_t | |||
{ | |||
public: | |||
size_t id { 0 }; // unique id of the field | |||
size_t dataset_id { 0 }; // unique id of the dataset type | |||
size_t real_dataset_id { 0 }; // unique id of the real/unwrapped dataset type | |||
@@ -86,6 +87,16 @@ beg_namespace_cpphibernate_driver_mariadb | |||
virtual void set (const data_context& context, const value_t&) const; | |||
virtual bool is_default (const data_context& context) const; | |||
virtual std::string generate_value (::cppmariadb::connection& connection) const; | |||
/* statements */ | |||
virtual ::cppmariadb::statement& get_statement_foreign_one_delete(bool key_known) const; | |||
virtual ::cppmariadb::statement& get_statement_foreign_many_update() const; | |||
protected: | |||
using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | |||
::cppmariadb::statement& get_statement_foreign_one_delete_impl(bool key_known, statement_ptr& known, statement_ptr& unknown) const; | |||
::cppmariadb::statement& get_statement_foreign_many_update_impl(statement_ptr& statement) const; | |||
}; | |||
/* simple_field_t */ | |||
@@ -181,8 +192,20 @@ beg_namespace_cpphibernate_driver_mariadb | |||
using base_type::base_type; | |||
public: | |||
/* CRUD */ | |||
virtual value_t foreign_create_update(const create_update_context& context) const override; | |||
virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const override; | |||
/* statements */ | |||
virtual ::cppmariadb::statement& get_statement_foreign_one_delete(bool key_known) const override; | |||
virtual ::cppmariadb::statement& get_statement_foreign_many_update() const override; | |||
private: | |||
using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | |||
mutable statement_ptr _statement_foreign_one_delete_key_known; | |||
mutable statement_ptr _statement_foreign_one_delete_key_unknown; | |||
mutable statement_ptr _statement_foreign_many_update; | |||
}; | |||
} |
@@ -108,6 +108,21 @@ beg_namespace_cpphibernate_driver_mariadb | |||
} | |||
} | |||
template<typename T_field> | |||
::cppmariadb::statement& foreign_table_field_t<T_field> | |||
::get_statement_foreign_one_delete(bool key_known) const | |||
{ | |||
return base_type::get_statement_foreign_one_delete_impl( | |||
key_known, | |||
_statement_foreign_one_delete_key_known, | |||
_statement_foreign_one_delete_key_unknown); | |||
} | |||
template<typename T_field> | |||
::cppmariadb::statement& foreign_table_field_t<T_field> | |||
::get_statement_foreign_many_update() const | |||
{ return base_type::get_statement_foreign_many_update_impl(_statement_foreign_many_update); } | |||
namespace __impl | |||
{ | |||
@@ -90,23 +90,27 @@ beg_namespace_cpphibernate_driver_mariadb | |||
friend struct table_polymorphic_t; | |||
using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | |||
using statement_map = std::map<size_t, ::cppmariadb::statement>; | |||
using map_key = std::tuple<size_t, const field_t*>; | |||
using statement_map = std::map<map_key, ::cppmariadb::statement>; | |||
mutable statement_ptr _statement_create_table; | |||
mutable statement_ptr _statement_alter_table; | |||
mutable statement_ptr _statement_insert_into; | |||
mutable statement_map _statement_select_static; | |||
mutable statement_map _statement_select_dynamic; | |||
mutable statement_map _statement_update; | |||
mutable statement_ptr _statement_foreign_many_delete; | |||
::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_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; | |||
std::string execute_create_update( | |||
const create_update_context& context, | |||
::cppmariadb::statement& statement, | |||
const filter_t* filter) const; | |||
::cppmariadb::statement& statement) const; | |||
virtual std::string create_update_base(const create_update_context& context) const; | |||
@@ -158,4 +158,116 @@ throw_not_implemented(read_context_ptr, foreign_read, const read_contex | |||
throw_not_implemented(bool, is_default, const data_context& context) | |||
throw_not_implemented(string, generate_value, ::cppmariadb::connection&) | |||
throw_not_implemented(value_t, get, const data_context& context) | |||
throw_not_implemented(void, set, const data_context& context, const value_t&) | |||
throw_not_implemented(void, set, const data_context& context, const value_t&) | |||
/* statements */ | |||
throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_one_delete, bool) | |||
throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_update) | |||
::cppmariadb::statement& field_t::get_statement_foreign_one_delete_impl(bool key_known, statement_ptr& known, statement_ptr& unknown) const | |||
{ | |||
assert(table); | |||
assert(table->primary_key_field); | |||
assert(referenced_table); | |||
assert(referenced_table->primary_key_field); | |||
if (key_known) | |||
{ | |||
if (!known) | |||
{ | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< ref_key_info.table_name | |||
<< "` WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "` FROM `" | |||
<< key_info.table_name | |||
<< "` WHERE `" | |||
<< key_info.field_name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< " AND `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "`!= " | |||
<< ref_key_info.convert_to_open | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close | |||
<< ")"; | |||
known.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *known; | |||
} | |||
else | |||
{ | |||
if (!unknown) | |||
{ | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< ref_key_info.table_name | |||
<< "` WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "` FROM `" | |||
<< key_info.table_name | |||
<< "` WHERE `" | |||
<< key_info.field_name | |||
<< "`=" | |||
<< key_info.convert_to_open | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< ")"; | |||
unknown.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *unknown; | |||
} | |||
} | |||
::cppmariadb::statement& field_t::get_statement_foreign_many_update_impl(statement_ptr& statement) const | |||
{ | |||
assert(referenced_table); | |||
assert(referenced_table->primary_key_field); | |||
if (!statement) | |||
{ | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
std::ostringstream os; | |||
os << "UPDATE `" | |||
<< ref_key_info.table_name | |||
<< "` SET `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "`=NULL, `" | |||
<< ref_key_info.table_name | |||
<< "_index_" | |||
<< field_name | |||
<< "`=0 WHERE `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_name | |||
<< "`=" | |||
<< ref_key_info.convert_to_open | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close; | |||
statement.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *statement; | |||
} |
@@ -910,8 +910,9 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
} | |||
/* type field for derived tables */ | |||
if (!table.derived_tables.empty() && | |||
!table.base_table) | |||
if ( !table.derived_tables.empty() | |||
&& !table.base_table | |||
&& !is_update) | |||
{ | |||
if (index++) | |||
os << ", "; | |||
@@ -948,13 +949,13 @@ std::string build_select_query( | |||
std::string table_t::execute_create_update( | |||
const create_update_context& context, | |||
::cppmariadb::statement& statement, | |||
const filter_t* filter) const | |||
::cppmariadb::statement& statement) const | |||
{ | |||
auto& connection = context.connection; | |||
auto& filter = context.filter; | |||
size_t index = 0; | |||
bool is_update = static_cast<bool>(filter); | |||
bool is_update = context.is_update; | |||
std::string primary_key; | |||
statement.clear(); | |||
@@ -976,7 +977,7 @@ std::string table_t::execute_create_update( | |||
/* base_key */ | |||
if ( base_table | |||
&& ( !is_update | |||
|| !filter->is_excluded(*base_table))) | |||
|| !filter.is_excluded(*base_table))) | |||
{ | |||
auto new_context = context; | |||
if (!new_context.derived_table) | |||
@@ -986,19 +987,37 @@ std::string table_t::execute_create_update( | |||
++index; | |||
} | |||
if (is_update && filter->is_excluded(*this)) | |||
if (is_update && filter.is_excluded(*this)) | |||
return primary_key; | |||
/* foreign table one fields */ | |||
for (auto& ptr : foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
if (is_update && filter->is_excluded(*ptr)) | |||
auto& field = *ptr; | |||
if (is_update && filter.is_excluded(field)) | |||
continue; | |||
value_t key = ptr->foreign_create_update(context); | |||
if (key.has_value()) statement.set(index, std::move(key)); | |||
else statement.set_null(index); | |||
/* insert/update dataset */ | |||
value_t key = field.foreign_create_update(context); | |||
if (key.has_value()) | |||
statement.set(index, std::move(key)); | |||
else if (field.value_is_nullable) | |||
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) */ | |||
if (context.is_update) | |||
{ | |||
auto& delete_statement = field.get_statement_foreign_one_delete(key.has_value()); | |||
delete_statement.set(0, primary_key); | |||
if (key.has_value()) | |||
delete_statement.set(1, *key); | |||
cpphibernate_debug_log("execute DELETE old foreign one query: " << delete_statement.query(connection)); | |||
connection.execute(delete_statement); | |||
} | |||
} | |||
/* foreign fields */ | |||
@@ -1036,7 +1055,7 @@ std::string table_t::execute_create_update( | |||
for (auto& ptr : data_fields) | |||
{ | |||
assert(ptr); | |||
if (is_update && filter->is_excluded(*ptr)) | |||
if (is_update && filter.is_excluded(*ptr)) | |||
continue; | |||
auto& field_info = *ptr; | |||
@@ -1048,8 +1067,9 @@ std::string table_t::execute_create_update( | |||
} | |||
/* type field for derived tables */ | |||
if (!derived_tables.empty() && | |||
!base_table) | |||
if ( !derived_tables.empty() | |||
&& !base_table | |||
&& !is_update) | |||
{ | |||
statement.set(index, context.derived_table | |||
? context.derived_table->table_id | |||
@@ -1084,6 +1104,8 @@ std::string table_t::execute_create_update( | |||
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); | |||
@@ -1092,16 +1114,39 @@ std::string table_t::execute_create_update( | |||
for (auto& ptr : foreign_table_many_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& field = *ptr; | |||
auto& ref_table = *field.referenced_table; | |||
if ( is_update | |||
&& ( filter->is_excluded(*ptr) | |||
|| filter->is_excluded(*ptr->referenced_table))) | |||
&& ( filter.is_excluded(field) | |||
|| filter.is_excluded(ref_table))) | |||
continue; | |||
/* set foreign keys of existing elements to null */ | |||
if (context.is_update) | |||
{ | |||
auto& update_statement = field.get_statement_foreign_many_update(); | |||
update_statement.set(0, primary_key); | |||
cpphibernate_debug_log("execute UPDATE old foreign many query: " << update_statement.query(connection)); | |||
connection.execute(update_statement); | |||
} | |||
/* update elements */ | |||
auto next_context = context; | |||
next_context.owner_field = ptr; | |||
next_context.owner_key = primary_key; | |||
next_context.derived_table = nullptr; | |||
ptr->foreign_create_update(next_context); | |||
field.foreign_create_update(next_context); | |||
/* 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); | |||
} | |||
} | |||
return primary_key; | |||
@@ -1210,15 +1255,56 @@ void table_t::emplace(const read_context& context) const | |||
auto& map = context.is_dynamic | |||
? _statement_select_dynamic | |||
: _statement_select_static; | |||
auto it = map.find(context.filter.cache_id); | |||
auto key = std::make_tuple(context.filter.cache_id, static_cast<const field_t*>(nullptr)); | |||
auto it = map.find(key); | |||
if (it == map.end()) | |||
{ | |||
auto query = build_select_query(*this, context.filter, context.is_dynamic); | |||
it = map.emplace(context.filter.cache_id, ::cppmariadb::statement(query)).first; | |||
it = map.emplace(key, ::cppmariadb::statement(query)).first; | |||
} | |||
return it->second; | |||
} | |||
::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); | |||
if (it == _statement_update.end()) | |||
{ | |||
auto query = build_create_update_query(*this, &filter, owner); | |||
it = _statement_update.emplace(key, ::cppmariadb::statement(query)).first; | |||
} | |||
return it->second; | |||
} | |||
::cppmariadb::statement& table_t::get_statement_foreign_many_delete() const | |||
{ | |||
if (!_statement_foreign_many_delete) | |||
{ | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< table_name | |||
<< "` WHERE"; | |||
auto first = true; | |||
for (auto ptr : foreign_key_fields) | |||
{ | |||
assert(ptr); | |||
auto& field = *ptr; | |||
if (first) | |||
first = false; | |||
else | |||
os << " AND"; | |||
os << " (`" | |||
<< field.table_name | |||
<< "_id_" | |||
<< field.field_name | |||
<< "` IS NULL)"; | |||
} | |||
_statement_foreign_many_delete.reset(new ::cppmariadb::statement(os.str())); | |||
} | |||
return *_statement_foreign_many_delete; | |||
} | |||
std::string table_t::create_update_base(const create_update_context& context) const | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
@@ -1244,8 +1330,10 @@ void table_t::init_stage2_exec(const init_context& context) const | |||
std::string table_t::create_update_exec(const create_update_context& context) const | |||
{ | |||
auto& statement = get_statement_insert_into(); | |||
return execute_create_update(context, statement, nullptr); | |||
auto& statement = context.is_update | |||
? get_statement_update(context.filter, context.owner_field) | |||
: get_statement_insert_into(); | |||
return execute_create_update(context, statement); | |||
} | |||
std::string table_t::create_update_intern(const create_update_context& context) const | |||
@@ -0,0 +1,525 @@ | |||
#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, update_test1) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test1` " | |||
"SET " | |||
"`str_data`='Xstr_data of class `test1` object `t1`X', " | |||
"`str64_data`='Xstr64_data of class `test1` object `t1`X', " | |||
"`u32_nullable`=null, " | |||
"`u32_ptr_u`='X456X', " | |||
"`u32_ptr_s`='X789X' " | |||
"WHERE " | |||
"`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')", | |||
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))); | |||
test1 t1; | |||
t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||
t1.str_data = "str_data of class `test1` object `t1`"; | |||
t1.str64_data = "str64_data of class `test1` object `t1`"; | |||
t1.u32_ptr_u = std::make_unique<uint32_t>(456); | |||
t1.u32_ptr_s = std::make_shared<uint32_t>(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(t1); | |||
} | |||
TEST(CppHibernateTests, update_test2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test2` " | |||
"SET " | |||
"`u8_data`='X1X', " | |||
"`i8_data`='X2X', " | |||
"`u16_data`='X3X', " | |||
"`i16_data`='X4X' " | |||
"WHERE " | |||
"`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')", | |||
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))); | |||
test2 t2; | |||
t2.id = uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"); | |||
t2.u8_data = 1; | |||
t2.i8_data = 2; | |||
t2.u16_data = 3; | |||
t2.i16_data = 4; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(t2); | |||
} | |||
TEST(CppHibernateTests, update_test3) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`u32_data`='X5X', " | |||
"`i32_data`='X6X', " | |||
"`u64_data`='X7X', " | |||
"`i64_data`='X8X' " | |||
"WHERE " | |||
"`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')", | |||
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))); | |||
test3 t3; | |||
t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||
t3.u32_data = 5; | |||
t3.i32_data = 6; | |||
t3.u64_data = 7; | |||
t3.i64_data = 8; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(t3); | |||
} | |||
TEST(CppHibernateTests, update_derived1) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_base` " | |||
"SET " | |||
"`name`='Xderived1X' " | |||
"WHERE " | |||
"`tbl_base_id`=UuidToBin('X3d12778a-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "SELECT Uuid()", result_used({ | |||
{ "e2488a64-b843-11e8-96f8-529269fb1459" } | |||
})); | |||
expect_query(mock, "INSERT INTO " | |||
"`tbl_test1` " | |||
"SET " | |||
"`tbl_test1_id`=UuidToBin('Xe2488a64-b843-11e8-96f8-529269fb1459X'), " | |||
"`str_data`='Xstr_data of class `test1` object `d1.test1_data`X', " | |||
"`str64_data`='Xstr64_data of class `test1` object `d1.test1_data`X', " | |||
"`u32_nullable`='X32X', " | |||
"`u32_ptr_u`=null, " | |||
"`u32_ptr_s`='X789X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test1` " | |||
"WHERE " | |||
"`tbl_test1_id` IN (" | |||
"SELECT " | |||
"`tbl_test1_id_test1_data` " | |||
"FROM " | |||
"`tbl_derived1` " | |||
"WHERE " | |||
"`tbl_derived1_id`=UuidToBin('X3d12758c-abb9-11e8-98d0-529269fb1459X') AND " | |||
"`tbl_test1_id_test1_data`!= UuidToBin('Xe2488a64-b843-11e8-96f8-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_derived1` " | |||
"SET " | |||
"`tbl_base_id`=UuidToBin('X3d12778a-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_test1_id_test1_data`=UuidToBin('Xe2488a64-b843-11e8-96f8-529269fb1459X'), " | |||
"`enum_data`='Xtest2X' " | |||
"WHERE " | |||
"`tbl_derived1_id`=UuidToBin('X3d12758c-abb9-11e8-98d0-529269fb1459X')", | |||
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))); | |||
derived1 d1; | |||
d1.id = uuid("3d12778a-abb9-11e8-98d0-529269fb1459"); | |||
d1.name = "derived1"; | |||
d1.derived1_id = uuid("3d12758c-abb9-11e8-98d0-529269fb1459"); | |||
d1.enum_data = test_enum::test2; | |||
d1.test1_data.str_data = "str_data of class `test1` object `d1.test1_data`"; | |||
d1.test1_data.str64_data = "str64_data of class `test1` object `d1.test1_data`"; | |||
d1.test1_data.u32_nullable = 32; | |||
d1.test1_data.u32_ptr_s = std::make_shared<uint32_t>(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(static_cast<base&>(d1)); | |||
} | |||
TEST(CppHibernateTests, update_derived2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_base` " | |||
"SET " | |||
"`name`='Xderived2X' " | |||
"WHERE " | |||
"`tbl_base_id`=UuidToBin('X3d127db6-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test2` " | |||
"SET " | |||
"`u8_data`='X10X', " | |||
"`i8_data`='X11X', " | |||
"`u16_data`='X12X', " | |||
"`i16_data`='X13X' " | |||
"WHERE " | |||
"`tbl_test2_id`=UuidToBin('X3d1283a6-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_nullable` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X') AND " | |||
"`tbl_test2_id_test2_nullable`!= UuidToBin('X3d1283a6-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "SELECT Uuid()", | |||
result_used({ | |||
{ "ec0f0aac-b8b9-11e8-96f8-529269fb1459" } | |||
})); | |||
expect_query(mock, "INSERT INTO " | |||
"`tbl_test2` " | |||
"SET " | |||
"`tbl_test2_id`=UuidToBin('Xec0f0aac-b8b9-11e8-96f8-529269fb1459X'), " | |||
"`u8_data`='X20X', " | |||
"`i8_data`='X21X', " | |||
"`u16_data`='X22X', " | |||
"`i16_data`='X23X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_ptr_u` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`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 " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_ptr_s` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_derived2` " | |||
"SET " | |||
"`tbl_base_id`=UuidToBin('X3d127db6-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_test2_id_test2_nullable`=UuidToBin('X3d1283a6-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_test2_id_test2_ptr_u`=UuidToBin('Xec0f0aac-b8b9-11e8-96f8-529269fb1459X'), " | |||
"`tbl_test2_id_test2_ptr_s`=UuidToBin(null) " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X')", | |||
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))); | |||
derived2 d2; | |||
d2.id = uuid("3d127db6-abb9-11e8-98d0-529269fb1459"); | |||
d2.name = "derived2"; | |||
d2.derived2_id = uuid("3d127bcc-abb9-11e8-98d0-529269fb1459"); | |||
d2.test2_nullable = test2 { }; | |||
d2.test2_nullable->id = uuid("3d1283a6-abb9-11e8-98d0-529269fb1459"); | |||
d2.test2_nullable->u8_data = 10; | |||
d2.test2_nullable->i8_data = 11; | |||
d2.test2_nullable->u16_data = 12; | |||
d2.test2_nullable->i16_data = 13; | |||
d2.test2_ptr_u = std::make_unique<test2>(); | |||
d2.test2_ptr_u->u8_data = 20; | |||
d2.test2_ptr_u->i8_data = 21; | |||
d2.test2_ptr_u->u16_data = 22; | |||
d2.test2_ptr_u->i16_data = 23; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(static_cast<base&>(d2)); | |||
} | |||
TEST(CppHibernateTests, update_derived3) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_base` " | |||
"SET " | |||
"`name`='Xderived3X' " | |||
"WHERE " | |||
"`tbl_base_id`=UuidToBin('X3d1288ce-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_nullable` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_ptr_u` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
"SELECT " | |||
"`tbl_test2_id_test2_ptr_s` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_derived2` " | |||
"SET " | |||
"`tbl_base_id`=UuidToBin('X3d1288ce-abb9-11e8-98d0-529269fb1459X'), " | |||
"`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('X3d1287a2-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_derived3` " | |||
"SET " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X') " | |||
"WHERE " | |||
"`tbl_derived3_id`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_test3_id_test3_list`=NULL, " | |||
"`tbl_test3_index_test3_list`=0 " | |||
"WHERE " | |||
"`tbl_test3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_derived3_index_test3_list`='X0X', " | |||
"`u32_data`='X100X', " | |||
"`i32_data`='X101X', " | |||
"`u64_data`='X102X', " | |||
"`i64_data`='X103X' " | |||
"WHERE " | |||
"`tbl_test3_id`=UuidToBin('X3d1289f0-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "SELECT Uuid()", | |||
result_used({ | |||
{ "435bd976-b8c3-11e8-96f8-529269fb1459" } | |||
})); | |||
expect_query(mock, "INSERT INTO " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_test3_id`=UuidToBin('X435bd976-b8c3-11e8-96f8-529269fb1459X'), " | |||
"`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_derived3_index_test3_list`='X1X', " | |||
"`tbl_derived3_id_test3_vector`=UuidToBin(null), " | |||
"`tbl_derived3_index_test3_vector`='X0X', " | |||
"`u32_data`='X110X', " | |||
"`i32_data`='X111X', " | |||
"`u64_data`='X112X', " | |||
"`i64_data`='X113X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
"`tbl_test3` " | |||
"WHERE " | |||
"(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
"(`tbl_derived3_id_test3_vector` IS NULL)"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_test3_id_test3_vector`=NULL, " | |||
"`tbl_test3_index_test3_vector`=0 " | |||
"WHERE " | |||
"`tbl_test3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')"); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_derived3_index_test3_vector`='X0X', " | |||
"`u32_data`='X200X', " | |||
"`i32_data`='X201X', " | |||
"`u64_data`='X202X', " | |||
"`i64_data`='X203X' " | |||
"WHERE " | |||
"`tbl_test3_id`=UuidToBin('X3d128eb4-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "SELECT Uuid()", | |||
result_used({ | |||
{ "1c0a3592-b8c4-11e8-96f8-529269fb1459" } | |||
})); | |||
expect_query(mock, "INSERT INTO " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_test3_id`=UuidToBin('X1c0a3592-b8c4-11e8-96f8-529269fb1459X'), " | |||
"`tbl_derived3_id_test3_list`=UuidToBin(null), " | |||
"`tbl_derived3_index_test3_list`='X0X', " | |||
"`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_derived3_index_test3_vector`='X1X', " | |||
"`u32_data`='X210X', " | |||
"`i32_data`='X211X', " | |||
"`u64_data`='X212X', " | |||
"`i64_data`='X213X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "UPDATE " | |||
"`tbl_test3` " | |||
"SET " | |||
"`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
"`tbl_derived3_index_test3_vector`='X2X', " | |||
"`u32_data`='X220X', " | |||
"`i32_data`='X221X', " | |||
"`u64_data`='X222X', " | |||
"`i64_data`='X223X' " | |||
"WHERE " | |||
"`tbl_test3_id`=UuidToBin('X3d129134-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE 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.id = uuid("3d1288ce-abb9-11e8-98d0-529269fb1459"); | |||
d3.name = "derived3"; | |||
d3.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); | |||
d3.derived3_id = uuid("3d12866c-abb9-11e8-98d0-529269fb1459"); | |||
d3.test3_list.emplace_back(); | |||
d3.test3_list.back().id = uuid("3d1289f0-abb9-11e8-98d0-529269fb1459"); | |||
d3.test3_list.back().u32_data = 100; | |||
d3.test3_list.back().i32_data = 101; | |||
d3.test3_list.back().u64_data = 102; | |||
d3.test3_list.back().i64_data = 103; | |||
d3.test3_list.emplace_back(); | |||
d3.test3_list.back().u32_data = 110; | |||
d3.test3_list.back().i32_data = 111; | |||
d3.test3_list.back().u64_data = 112; | |||
d3.test3_list.back().i64_data = 113; | |||
d3.test3_vector.emplace_back(); | |||
d3.test3_vector.back().id = uuid("3d128eb4-abb9-11e8-98d0-529269fb1459"); | |||
d3.test3_vector.back().u32_data = 200; | |||
d3.test3_vector.back().i32_data = 201; | |||
d3.test3_vector.back().u64_data = 202; | |||
d3.test3_vector.back().i64_data = 203; | |||
d3.test3_vector.emplace_back(); | |||
d3.test3_vector.back().u32_data = 210; | |||
d3.test3_vector.back().i32_data = 211; | |||
d3.test3_vector.back().u64_data = 212; | |||
d3.test3_vector.back().i64_data = 213; | |||
d3.test3_vector.emplace_back(); | |||
d3.test3_vector.back().id = uuid("3d129134-abb9-11e8-98d0-529269fb1459"); | |||
d3.test3_vector.back().u32_data = 220; | |||
d3.test3_vector.back().i32_data = 221; | |||
d3.test3_vector.back().u64_data = 222; | |||
d3.test3_vector.back().i64_data = 223; | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(static_cast<derived2&>(d3)); | |||
} |