@@ -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; | |||
@@ -72,11 +72,11 @@ beg_namespace_cpphibernate_schema | |||
/* getter_lambda_t */ | |||
template<typename T_dataset, typename T_lambda> | |||
template<typename T_lambda, typename T_dataset, typename T_value> | |||
struct getter_lambda_t | |||
: public getter_t<T_dataset, decltype(std::declval<T_lambda>()(std::declval<T_dataset>()))> | |||
: public getter_t<T_dataset, T_value> | |||
{ | |||
using base_type = getter_t<T_dataset, decltype(std::declval<T_lambda>()(std::declval<T_dataset>()))>; | |||
using base_type = getter_t<T_dataset, T_value>; | |||
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<typename T> | |||
struct is_getter_impl<T, mp::enable_if_c< | |||
mp::is_base_of<getter_t<typename T::dataset_type, typename T::value_type>, T>::value>> | |||
struct is_getter_impl<T, mp::enable_if< | |||
mp::is_base_of<getter_t<typename T::dataset_type, typename T::value_type>, T>>> | |||
: public mp::c_true_t | |||
{ }; | |||
@@ -122,19 +122,31 @@ beg_namespace_cpphibernate_schema | |||
template<typename T_dataset, typename T_value> | |||
constexpr decltype(auto) make_getter_member_var(T_value T_dataset::* member) | |||
{ return __impl::getter_member_var_t<T_dataset, T_value, T_value T_dataset::*>(member); } | |||
{ | |||
using getter_type = __impl::getter_member_var_t<T_dataset, T_value, T_value T_dataset::*>; | |||
return getter_type(member); | |||
} | |||
template<typename T_dataset, typename T_value> | |||
constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)()) | |||
{ return __impl::getter_member_func_t<T_dataset, T_value, T_value (T_dataset::*)()>(member); } | |||
{ | |||
using getter_type = __impl::getter_member_func_t<T_dataset, T_value, T_value (T_dataset::*)()>; | |||
return getter_type(member); | |||
} | |||
template<typename T_dataset, typename T_value> | |||
constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)() const) | |||
{ return __impl::getter_member_func_t<const T_dataset, T_value, T_value (T_dataset::*)() const>(member); } | |||
{ | |||
using getter_type = __impl::getter_member_func_t<const T_dataset, T_value, T_value (T_dataset::*)() const>; | |||
return getter_type(member); | |||
} | |||
template<typename T_dataset, typename T_lambda> | |||
constexpr decltype(auto) make_getter_lambda(T_lambda&& lambda, boost::hana::basic_type<T_dataset>) | |||
{ return __impl::getter_lambda_t<T_dataset, T_lambda>(std::forward<T_lambda>(lambda)); } | |||
template<typename T_lambda, typename T_wrapped_dataset, typename T_wrapped_value> | |||
constexpr decltype(auto) make_getter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) | |||
{ | |||
using getter_type = __impl::getter_lambda_t<T_lambda, misc::decay_unwrap_t<T_wrapped_dataset>, misc::decay_unwrap_t<T_wrapped_value>>; | |||
return getter_type(std::forward<T_lambda>(lambda)); | |||
} | |||
/* operations */ | |||
@@ -160,6 +172,11 @@ beg_namespace_cpphibernate_schema | |||
template<typename T_func, typename T_dataset, typename T_value> | |||
constexpr decltype(auto) operator()(T_func&& func, hana::type<T_dataset>&& wrapped_dataset, hana::type<T_value>&& wrapped_value) const | |||
{ return make_getter_lambda(std::forward<T_func>(func), std::forward<hana::type<T_dataset>>(wrapped_dataset), std::forward<hana::type<T_value>>(wrapped_value)); } | |||
template<typename T_getter> | |||
constexpr auto operator()(T_getter&& getter) const | |||
-> mp::enable_if<is_getter<T_getter>, T_getter> | |||
{ return std::forward<T_getter>(getter); } | |||
}; | |||
} | |||
@@ -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<p_dataset>, \ | |||
::boost::hana::type_c<p_value>), \ | |||
::cpphibernate::schema::make_setter_lambda( \ | |||
[](p_dataset&, p_value&&) { }, \ | |||
::boost::hana::type_c<p_dataset>, \ | |||
::boost::hana::type_c<p_value>), \ | |||
cpphibernate::schema::attribute::primary_key) |
@@ -11,20 +11,21 @@ beg_namespace_cpphibernate_schema | |||
/* setter_t */ | |||
template<typename T_value> | |||
template<typename T_dataset, typename T_value> | |||
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<void> | |||
: public setter_t<void, void> | |||
{ | |||
using base_type = setter_t<void>; | |||
using value_type = typename base_type::value_type; | |||
using base_type = setter_t<void, void>; | |||
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<typename T_dataset, typename T_value, typename T_member> | |||
struct setter_member_var_t | |||
: public setter_t<T_value> | |||
: public setter_t<T_dataset, T_value> | |||
{ | |||
using base_type = setter_t<T_value>; | |||
using base_type = setter_t<T_dataset, T_value>; | |||
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<typename T_dataset, typename T_value, typename T_member> | |||
struct setter_member_func_t | |||
: public setter_t<T_value> | |||
: public setter_t<T_dataset, T_value> | |||
{ | |||
using base_type = setter_t<T_value>; | |||
using base_type = setter_t<T_dataset, T_value>; | |||
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<typename T_dataset, typename T_value, typename T_lambda> | |||
template<typename T_lambda, typename T_dataset, typename T_value> | |||
struct setter_lambda_t | |||
: public setter_t<T_value> | |||
: public setter_t<T_dataset, T_value> | |||
{ | |||
using base_type = setter_t<T_value>; | |||
using base_type = setter_t<T_dataset, T_value>; | |||
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<typename T> | |||
struct is_setter_impl<T, mp::enable_if_c< | |||
mp::is_base_of<setter_t<typename T::value_type>, T>::value>> | |||
struct is_setter_impl<T, mp::enable_if< | |||
mp::is_base_of<setter_t<typename T::dataset_type, typename T::value_type>, 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<typename T_dataset, typename T_value> | |||
constexpr decltype(auto) make_setter_member_var(T_value T_dataset::* member) | |||
{ return __impl::setter_member_var_t<T_dataset, T_value, T_value T_dataset::*>(member); } | |||
{ | |||
using setter_type = __impl::setter_member_var_t<T_dataset, T_value, T_value T_dataset::*>; | |||
return setter_type(member); | |||
} | |||
template<typename T_dataset, typename T_value, typename T_return> | |||
constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value)) | |||
{ return __impl::setter_member_func_t<T_dataset, T_value, T_return (T_dataset::*)(T_value)>(member); } | |||
{ | |||
using setter_type = __impl::setter_member_func_t<T_dataset, T_value, T_return (T_dataset::*)(T_value)>; | |||
return setter_type(member); | |||
} | |||
template<typename T_dataset, typename T_value, typename T_return> | |||
constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value) const) | |||
{ return __impl::setter_member_func_t<const T_dataset, T_value, T_return (T_dataset::*)(T_value) const>(member); } | |||
{ | |||
using setter_type = __impl::setter_member_func_t<const T_dataset, T_value, T_return (T_dataset::*)(T_value) const>; | |||
return setter_type(member); | |||
} | |||
template<typename T_dataset, typename T_value, typename T_lambda> | |||
constexpr decltype(auto) make_setter_lambda(T_lambda&& lambda, boost::hana::basic_type<T_dataset>, boost::hana::basic_type<T_value>) | |||
{ return __impl::setter_lambda_t<T_dataset, T_value, T_lambda>(std::forward<T_lambda>(lambda)); } | |||
template<typename T_lambda, typename T_wrapped_dataset, typename T_wrapped_value> | |||
constexpr decltype(auto) make_setter_lambda(T_lambda&& lambda, T_wrapped_dataset&&, T_wrapped_value&&) | |||
{ | |||
using setter_type = __impl::setter_lambda_t<T_lambda, misc::decay_unwrap_t<T_wrapped_dataset>, misc::decay_unwrap_t<T_wrapped_value>>; | |||
return setter_type(std::forward<T_lambda>(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<typename T_func, typename T_dataset, typename T_value> | |||
constexpr decltype(auto) operator()(T_func&& func, hana::type<T_dataset>&& wrapped_dataset, hana::type<T_value>&& wrapped_value) const | |||
{ return make_setter_lambda(std::forward<T_func>(func), std::forward<hana::type<T_dataset>>(wrapped_dataset), std::forward<hana::type<T_value>>(wrapped_value)); } | |||
template<typename T_func, typename T_wrapped_dataset, typename T_wrapped_value> | |||
constexpr decltype(auto) operator()(T_func&& func, T_wrapped_dataset&&, T_wrapped_value&&) const | |||
{ return make_setter_lambda(std::forward<T_func>(func), T_wrapped_dataset { }, T_wrapped_value { }); } | |||
template<typename T_setter> | |||
constexpr auto operator()(T_setter&& setter) const | |||
-> mp::enable_if<is_setter<T_setter>, T_setter> | |||
{ return std::forward<T_setter>(setter); } | |||
}; | |||
} | |||
@@ -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); | |||
@@ -449,3 +449,72 @@ TEST(CppHibernateTests, create_derived3) | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.create(static_cast<derived2&>(d3)); | |||
} | |||
TEST(CppHibernateTests, create_dummy_owner) | |||
{ | |||
StrictMock<mariadb_mock> 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<MYSQL*>(0x1111), _, _, _)) | |||
.Times(AnyNumber()) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
dummy_owner d; | |||
d.dummies.emplace_back(123); | |||
d.dummies.emplace_back(456); | |||
d.dummies.emplace_back(789); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.create(d); | |||
} |
@@ -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( | |||
@@ -863,4 +863,58 @@ TEST(CppHibernateTests, read_base_ptr_vector_dynamic) | |||
} | |||
EXPECT_EQ(bIt, base_vec.end()); | |||
} | |||
TEST(CppHibernateTests, read_dummy_owner) | |||
{ | |||
StrictMock<mariadb_mock> 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<MYSQL*>(0x1111))); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_real_escape_string(reinterpret_cast<MYSQL*>(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<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(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); | |||
} |
@@ -634,4 +634,79 @@ TEST(CppHibernateTests, update_dynamic_base) | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(b); | |||
} | |||
TEST(CppHibernateTests, update_dummy_owner) | |||
{ | |||
StrictMock<mariadb_mock> 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<MYSQL*>(0x1111), _, _, _)) | |||
.Times(AnyNumber()) | |||
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(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<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.update(d); | |||
} |
@@ -93,6 +93,21 @@ struct derived3 | |||
std::vector<test3> 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<dummy_id> 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) | |||
) | |||
); |