Browse Source

* implemented temporary id field for datasets without id member

master
bergmann 5 years ago
parent
commit
296e8dc983
10 changed files with 426 additions and 89 deletions
  1. +3
    -3
      include/cpphibernate/driver/mariadb/schema/table.h
  2. +28
    -11
      include/cpphibernate/schema/getter.h
  3. +15
    -0
      include/cpphibernate/schema/macros.h
  4. +49
    -28
      include/cpphibernate/schema/setter.h
  5. +75
    -47
      src/cpphibernate/driver/mariadb/schema/table.cpp
  6. +69
    -0
      test/cpphibernate_create.cpp
  7. +29
    -0
      test/cpphibernate_init.cpp
  8. +54
    -0
      test/cpphibernate_read.cpp
  9. +75
    -0
      test/cpphibernate_update.cpp
  10. +29
    -0
      test/test_schema.h

+ 3
- 3
include/cpphibernate/driver/mariadb/schema/table.h View File

@@ -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;


+ 28
- 11
include/cpphibernate/schema/getter.h View File

@@ -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); }
};

}


+ 15
- 0
include/cpphibernate/schema/macros.h View File

@@ -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)

+ 49
- 28
include/cpphibernate/schema/setter.h View File

@@ -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); }
};

}


+ 75
- 47
src/cpphibernate/driver/mariadb/schema/table.cpp View File

@@ -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);


+ 69
- 0
test/cpphibernate_create.cpp View File

@@ -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);
}

+ 29
- 0
test/cpphibernate_init.cpp View File

@@ -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(


+ 54
- 0
test/cpphibernate_read.cpp View File

@@ -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);
}

+ 75
- 0
test/cpphibernate_update.cpp View File

@@ -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);
}

+ 29
- 0
test/test_schema.h View File

@@ -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)
)
);

Loading…
Cancel
Save