@@ -195,5 +195,24 @@ beg_namespace_cpphibernate_driver_mariadb | |||
using read_context_ptr = std::unique_ptr<read_context>; | |||
/* destroy_context */ | |||
struct destroy_context | |||
: public data_context | |||
{ | |||
std::string where; | |||
template<typename T_data> | |||
inline destroy_context( | |||
T_data& p_data, | |||
const schema_t& p_schema, | |||
::cppmariadb::connection& p_connection) | |||
: data_context( | |||
p_data, | |||
p_schema, | |||
p_connection) | |||
{ } | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -1,6 +1,7 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/impl/create_update.h> | |||
#include <cpphibernate/driver/mariadb/impl/destroy.h> | |||
#include <cpphibernate/driver/mariadb/impl/limit.h> | |||
#include <cpphibernate/driver/mariadb/impl/order_by.h> | |||
#include <cpphibernate/driver/mariadb/impl/read.h> | |||
@@ -0,0 +1,96 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* destroy_impl_t */ | |||
template<typename T_dataset, typename = void> | |||
struct destroy_impl_t | |||
{ | |||
using dataset_type = T_dataset; | |||
static inline void apply(const destroy_context& context, bool strict = true) | |||
{ | |||
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
auto& connection = context.connection; | |||
auto& schema = context.schema; | |||
auto& table = schema.table(dataset_id); | |||
assert(table.primary_key_field); | |||
transaction_lock trans(connection); | |||
if (!table.primary_key_field->is_default(context)) | |||
{ | |||
table.destroy(context); | |||
} | |||
else if (strict) | |||
{ | |||
throw misc::hibernate_exception("can not destroy dataset with no primary key assigned!"); | |||
} | |||
trans.commit(); | |||
} | |||
}; | |||
/* destroy_impl_t - nullable */ | |||
template<typename T_dataset> | |||
struct destroy_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_nullable<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||
static inline void apply(const destroy_context& context, bool strict = true) | |||
{ | |||
auto& dataset = context.get<dataset_type>(); | |||
auto* value = nullable_helper_type::get(dataset); | |||
if (value) | |||
{ | |||
using new_dataset_type = mp::decay_t<decltype(*value)>; | |||
using new_destroy_impl_type = destroy_impl_t<new_dataset_type>; | |||
auto new_context = change_context(context, *value); | |||
new_destroy_impl_type::apply(new_context, strict); | |||
} | |||
else if (strict) | |||
{ | |||
throw misc::hibernate_exception("can not destroy nullable type with no value!"); | |||
} | |||
} | |||
}; | |||
/* destroy_impl_t - container */ | |||
template<typename T_dataset> | |||
struct destroy_impl_t< | |||
T_dataset, | |||
mp::enable_if<misc::is_container<T_dataset>>> | |||
{ | |||
using dataset_type = T_dataset; | |||
static inline void apply(const destroy_context& context, bool strict = true) | |||
{ | |||
auto& connection = context.connection; | |||
auto& dataset = context.get<dataset_type>(); | |||
transaction_lock trans(connection); | |||
for (auto& x : dataset) | |||
{ | |||
using new_dataset_type = mp::decay_t<decltype(x)>; | |||
using new_destroy_impl_type = destroy_impl_t<new_dataset_type>; | |||
auto new_context = change_context(context, x); | |||
new_destroy_impl_type::apply(new_context, strict); | |||
} | |||
trans.commit(); | |||
} | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -65,6 +65,21 @@ beg_namespace_cpphibernate_driver_mariadb | |||
create_update_impl_t<T_dataset>::apply( | |||
create_update_context(dataset, _schema, _connection, _filter, true)); | |||
} | |||
template<typename T_dataset> | |||
inline void destroy_impl(T_dataset& dataset) const | |||
{ | |||
using dataset_type = mp::decay_t<T_dataset>; | |||
using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
auto& table = _schema.table(dataset_id); | |||
destroy_context context(dataset, _schema, _connection); | |||
context.where = table.get_where_primary_key(context); | |||
destroy_impl_t<T_dataset>::apply(context); | |||
} | |||
}; | |||
} |
@@ -67,7 +67,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
const table_t* get_derived_by_table_id(size_t id) const; | |||
const table_t* get_derived_by_dataset_id(size_t id) const; | |||
virtual void emplace(const read_context& context) const; | |||
virtual void emplace (const read_context& context) const; | |||
std::string get_where_primary_key(const data_context& context) const; | |||
std::string build_delete_query (const std::string* where) const; | |||
/* CRUD */ | |||
inline void init_stage1(const init_context& context) const | |||
@@ -82,6 +84,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
inline void read(const read_context& context) const | |||
{ return read_exec(context); } | |||
inline void destroy(const destroy_context& context) const | |||
{ return destroy_intern(context); } | |||
private: | |||
template<typename T_schema, typename T_table, typename T_base_dataset> | |||
friend struct table_simple_t; | |||
@@ -100,6 +105,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
mutable statement_map _statement_select_dynamic; | |||
mutable statement_map _statement_update; | |||
mutable statement_ptr _statement_foreign_many_delete; | |||
mutable statement_ptr _statement_delete; | |||
::cppmariadb::statement& get_statement_create_table() const; | |||
::cppmariadb::statement* get_statement_alter_table() const; | |||
@@ -107,6 +113,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
::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; | |||
::cppmariadb::statement& get_statement_delete() const; | |||
void execute_foreign_many_delete(const base_context& context) const; | |||
std::string execute_create_update( | |||
const create_update_context& context, | |||
@@ -122,6 +131,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||
std::string create_update_exec (const create_update_context& context) const; | |||
void read_exec (const read_context& context) const; | |||
virtual void destroy_intern (const destroy_context& context) const; | |||
void destroy_exec (const destroy_context& context) const; | |||
void destroy_cleanup (const base_context& context, bool check_derived, bool check_base) const; | |||
}; | |||
/* table_simple_t */ | |||
@@ -177,6 +190,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
protected: | |||
virtual std::string create_update_intern(const create_update_context& context) const override; | |||
virtual void destroy_intern (const destroy_context& context) const override; | |||
private: | |||
virtual std::string create_update_base(const create_update_context& context) const override; | |||
@@ -64,6 +64,36 @@ beg_namespace_cpphibernate_driver_mariadb | |||
: this->create_update_exec(context); | |||
} | |||
template<typename T_schema, typename T_table, typename T_base_dataset> | |||
void table_polymorphic_t<T_schema, T_table, T_base_dataset> | |||
::destroy_intern(const destroy_context& context) const | |||
{ | |||
bool done = false; | |||
auto& dataset = context.get<dataset_type>(); | |||
for_each_derived(dataset, hana::false_c, [&](auto& derived_dataset){ | |||
if (!done) | |||
{ | |||
using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>; | |||
auto derived_dataset_id = misc::get_type_id(hana::type_c<derived_dataset_type>); | |||
auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); | |||
if (!derived_table) | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
<< "unable to find derived table info for dataset '" | |||
<< utl::type_helper<derived_dataset_type>::name() << "'!").str()); | |||
} | |||
auto new_context = change_context(context, derived_dataset); | |||
derived_table->destroy(new_context); | |||
done = true; | |||
} | |||
}); | |||
if (!done) | |||
{ | |||
this->destroy_exec(context); | |||
} | |||
} | |||
template<typename T_schema, typename T_table, typename T_base_dataset> | |||
std::string table_polymorphic_t<T_schema, T_table, T_base_dataset> | |||
::create_update_base(const create_update_context& context) const | |||
@@ -176,13 +176,12 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
{ | |||
if (!known) | |||
{ | |||
auto& ref_table = *referenced_table; | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< ref_key_info.table_name | |||
<< "` WHERE `" | |||
os << "WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
@@ -205,7 +204,10 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
<< "?\?" | |||
<< ref_key_info.convert_to_close | |||
<< ")"; | |||
known.reset(new ::cppmariadb::statement(os.str())); | |||
auto where = os.str(); | |||
auto query = ref_table.build_delete_query(&where); | |||
known.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *known; | |||
} | |||
@@ -213,13 +215,12 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
{ | |||
if (!unknown) | |||
{ | |||
auto& ref_table = *referenced_table; | |||
auto& key_info = *table->primary_key_field; | |||
auto& ref_key_info = *referenced_table->primary_key_field; | |||
auto& ref_key_info = *ref_table.primary_key_field; | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< ref_key_info.table_name | |||
<< "` WHERE `" | |||
os << "WHERE `" | |||
<< ref_key_info.field_name | |||
<< "` IN (SELECT `" | |||
<< ref_key_info.table_name | |||
@@ -234,7 +235,10 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||
<< "?\?" | |||
<< key_info.convert_to_close | |||
<< ")"; | |||
unknown.reset(new ::cppmariadb::statement(os.str())); | |||
auto where = os.str(); | |||
auto query = ref_table.build_delete_query(&where); | |||
unknown.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *unknown; | |||
} | |||
@@ -443,6 +443,160 @@ struct select_query_builder_t | |||
} | |||
}; | |||
/* delete_query_builder_t */ | |||
struct delete_query_builder_t | |||
{ | |||
const table_t& _table; | |||
size_t alias_id { 0 }; | |||
std::ostringstream os; | |||
std::list<std::string> names; | |||
delete_query_builder_t( | |||
const table_t& p_table) | |||
: _table (p_table) | |||
{ } | |||
inline std::string make_alias() | |||
{ return std::string("T") + std::to_string(alias_id++); } | |||
inline void add_table(const table_t& table, const std::string& alias, bool add_base, bool add_derived) | |||
{ | |||
auto has_alias = !alias.empty(); | |||
auto real_alias = has_alias ? alias : table.table_name; | |||
if (table.base_table && add_base) | |||
{ | |||
assert(table.base_table->primary_key_field); | |||
auto& base_table = *table.base_table; | |||
auto& base_key = *base_table.primary_key_field; | |||
auto base_alias = has_alias ? make_alias() : std::string(); | |||
auto real_base_alias = has_alias ? base_alias : base_table.table_name; | |||
os << " LEFT JOIN `" | |||
<< base_table.table_name; | |||
if (has_alias) | |||
{ | |||
os << "` AS `" | |||
<< base_alias; | |||
} | |||
os << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< base_key.field_name | |||
<< "`=`" | |||
<< real_base_alias | |||
<< "`.`" | |||
<< base_key.field_name | |||
<< "`"; | |||
add_table(base_table, base_alias, true, false); | |||
} | |||
names.emplace_back(real_alias); | |||
/* foreign table one */ | |||
for (auto& ptr : table.foreign_table_one_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
assert(ptr->referenced_table->primary_key_field); | |||
auto& field = *ptr; | |||
auto& ref_table = *field.referenced_table; | |||
auto& ref_key = *ref_table.primary_key_field; | |||
auto new_alias = make_alias(); | |||
os << " LEFT JOIN `" | |||
<< ref_table.table_name | |||
<< "` AS `" | |||
<< new_alias | |||
<< "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< ref_key.table_name | |||
<< "_id_" | |||
<< field.field_name | |||
<< "`=`" | |||
<< new_alias | |||
<< "`.`" | |||
<< ref_key.field_name | |||
<< "`"; | |||
add_table(ref_table, new_alias, true, true); | |||
} | |||
/* derived tables */ | |||
if (add_derived) | |||
{ | |||
for (auto& ptr : table.derived_tables) | |||
{ | |||
assert(ptr); | |||
assert(ptr->primary_key_field); | |||
auto& derived_table = *ptr; | |||
auto& primary_key = *table.primary_key_field; | |||
auto derived_alias = has_alias ? make_alias() : std::string(); | |||
auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name; | |||
os << " LEFT JOIN `" | |||
<< derived_table.table_name; | |||
if (has_alias) | |||
{ | |||
os << "` AS `" | |||
<< derived_alias; | |||
} | |||
os << "` ON `" | |||
<< real_alias | |||
<< "`.`" | |||
<< primary_key.field_name | |||
<< "`=`" | |||
<< real_derived_alias | |||
<< "`.`" | |||
<< primary_key.field_name | |||
<< "`"; | |||
add_table(derived_table, derived_alias, false, true); | |||
} | |||
} | |||
} | |||
inline std::string operator()(const std::string* where) | |||
{ | |||
os << " FROM `" | |||
<< _table.table_name | |||
<< "`"; | |||
add_table(_table, "", true, true); | |||
if (where) | |||
{ | |||
os << " " << *where; | |||
} | |||
else | |||
{ | |||
os << " ?where!"; | |||
} | |||
std::ostringstream ss; | |||
ss << "DELETE "; | |||
bool first = true; | |||
for (auto& name : names) | |||
{ | |||
if (first) | |||
first = false; | |||
else | |||
ss << ", "; | |||
ss << "`" | |||
<< name | |||
<< "`"; | |||
} | |||
ss << os.str(); | |||
return ss.str(); | |||
} | |||
}; | |||
/* build queries */ | |||
std::string build_init_stage1_query(const table_t& table) | |||
@@ -686,7 +840,7 @@ std::string build_init_stage2_query(const table_t& table) | |||
<< indent | |||
<< "ON DELETE CASCADE" | |||
<< indent | |||
<< "ON UPDATE NO ACTION" | |||
<< "ON UPDATE CASCADE" | |||
<< decindent; | |||
} | |||
@@ -723,7 +877,7 @@ std::string build_init_stage2_query(const table_t& table) | |||
<< ref_key_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "ON DELETE CASCADE" | |||
<< "ON DELETE SET NULL" | |||
<< indent | |||
<< "ON UPDATE NO ACTION" | |||
<< decindent; | |||
@@ -1143,9 +1297,7 @@ std::string table_t::execute_create_update( | |||
/* 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); | |||
ref_table.execute_foreign_many_delete(context); | |||
} | |||
} | |||
@@ -1220,6 +1372,31 @@ const table_t* table_t::get_derived_by_dataset_id(size_t id) const | |||
void table_t::emplace(const read_context& context) const | |||
{ throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); } | |||
std::string table_t::get_where_primary_key(const data_context& context) const | |||
{ | |||
assert(primary_key_field); | |||
auto& field = *primary_key_field; | |||
auto primary_key = *field.get(context); | |||
std::ostringstream os; | |||
os << "WHERE `" | |||
<< field.table_name | |||
<< "`.`" | |||
<< field.field_name | |||
<< "`=" | |||
<< field.convert_to_open | |||
<< "'" | |||
<< context.connection.escape(primary_key) | |||
<< "'" | |||
<< field.convert_to_close; | |||
return os.str(); | |||
} | |||
std::string table_t::build_delete_query(const std::string* where) const | |||
{ return delete_query_builder_t(*this)(where); } | |||
::cppmariadb::statement& table_t::get_statement_create_table() const | |||
{ | |||
if (_statement_create_table) | |||
@@ -1282,10 +1459,8 @@ void table_t::emplace(const read_context& context) const | |||
if (!_statement_foreign_many_delete) | |||
{ | |||
std::ostringstream os; | |||
os << "DELETE FROM `" | |||
<< table_name | |||
<< "` WHERE"; | |||
auto first = true; | |||
os << "WHERE"; | |||
for (auto ptr : foreign_key_fields) | |||
{ | |||
assert(ptr); | |||
@@ -1300,11 +1475,32 @@ void table_t::emplace(const read_context& context) const | |||
<< field.field_name | |||
<< "` IS NULL)"; | |||
} | |||
_statement_foreign_many_delete.reset(new ::cppmariadb::statement(os.str())); | |||
std::string where = os.str(); | |||
auto query = delete_query_builder_t(*this)(&where); | |||
_statement_foreign_many_delete.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *_statement_foreign_many_delete; | |||
} | |||
::cppmariadb::statement& table_t::get_statement_delete() const | |||
{ | |||
if (!_statement_delete) | |||
{ | |||
auto query = delete_query_builder_t(*this)(nullptr); | |||
_statement_delete.reset(new ::cppmariadb::statement(query)); | |||
} | |||
return *_statement_delete; | |||
} | |||
void table_t::execute_foreign_many_delete(const base_context& context) const | |||
{ | |||
auto& connection = context.connection; | |||
auto& statement = get_statement_foreign_many_delete(); | |||
cpphibernate_debug_log("execute DELETE old foreign many query: " << statement.query(connection)); | |||
connection.execute(statement); | |||
} | |||
std::string table_t::create_update_base(const create_update_context& context) const | |||
{ | |||
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||
@@ -1328,6 +1524,9 @@ void table_t::init_stage2_exec(const init_context& context) const | |||
connection.execute(*statement); | |||
} | |||
std::string table_t::create_update_intern(const create_update_context& context) const | |||
{ return create_update_exec(context); } | |||
std::string table_t::create_update_exec(const create_update_context& context) const | |||
{ | |||
auto& statement = context.is_update | |||
@@ -1336,9 +1535,6 @@ std::string table_t::create_update_exec(const create_update_context& context) co | |||
return execute_create_update(context, statement); | |||
} | |||
std::string table_t::create_update_intern(const create_update_context& context) const | |||
{ return create_update_exec(context); } | |||
void table_t::read_exec(const read_context& context) const | |||
{ | |||
auto& statement = get_statement_select(context); | |||
@@ -1401,4 +1597,58 @@ void table_t::read_exec(const read_context& context) const | |||
ref_table.read(next_context); | |||
} | |||
} | |||
void table_t::destroy_intern(const destroy_context& context) const | |||
{ return destroy_exec(context); } | |||
void table_t::destroy_exec(const destroy_context& context) const | |||
{ | |||
assert(primary_key_field); | |||
auto& connection = context.connection; | |||
auto& statement = get_statement_delete(); | |||
statement.set(0, context.where); | |||
cpphibernate_debug_log("execute DELETE query: " << statement.query(connection)); | |||
connection.execute(statement); | |||
destroy_cleanup(context, false, true); | |||
} | |||
void table_t::destroy_cleanup(const base_context& context, bool check_derived, bool check_base) const | |||
{ | |||
for (auto ptr : foreign_table_many_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& ref_table = *ptr->referenced_table; | |||
ref_table.execute_foreign_many_delete(context); | |||
} | |||
for (auto ptr : foreign_table_fields) | |||
{ | |||
assert(ptr); | |||
assert(ptr->referenced_table); | |||
auto& ref_table = *ptr->referenced_table; | |||
ref_table.destroy_cleanup(context, true, true); | |||
} | |||
if (check_base && base_table) | |||
{ | |||
base_table->destroy_cleanup(context, false, true); | |||
} | |||
if (check_derived) | |||
{ | |||
for (auto ptr : derived_tables) | |||
{ | |||
assert(ptr); | |||
ptr->destroy_cleanup(context, true, false); | |||
} | |||
} | |||
} |
@@ -0,0 +1,250 @@ | |||
#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, destroy_test1) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_test1` " | |||
"FROM " | |||
"`tbl_test1` " | |||
"WHERE " | |||
"`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')"); | |||
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"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(t1); | |||
} | |||
TEST(CppHibernateTests, destroy_test2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2`.`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')"); | |||
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"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(t2); | |||
} | |||
TEST(CppHibernateTests, destroy_test3) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_test3` " | |||
"FROM " | |||
"`tbl_test3` " | |||
"WHERE " | |||
"`tbl_test3`.`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')"); | |||
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"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(t3); | |||
} | |||
TEST(CppHibernateTests, destroy_derived1) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_base`, " | |||
"`tbl_derived1`, " | |||
"`T0` " | |||
"FROM " | |||
"`tbl_derived1` " | |||
"LEFT JOIN " | |||
"`tbl_base` ON `tbl_derived1`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
"LEFT JOIN " | |||
"`tbl_test1` AS `T0` ON `tbl_derived1`.`tbl_test1_id_test1_data`=`T0`.`tbl_test1_id` " | |||
"WHERE " | |||
"`tbl_base`.`tbl_base_id`=UuidToBin('X3d12778a-abb9-11e8-98d0-529269fb1459X')"); | |||
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"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(static_cast<base&>(d1)); | |||
} | |||
TEST(CppHibernateTests, destroy_derived2) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_base`, " | |||
"`tbl_derived2`, " | |||
"`T0`, " | |||
"`T1`, " | |||
"`T2`, " | |||
"`tbl_derived3` " | |||
"FROM " | |||
"`tbl_derived2` " | |||
"LEFT JOIN " | |||
"`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` " | |||
"LEFT JOIN " | |||
"`tbl_derived3` ON `tbl_derived2`.`tbl_derived2_id`=`tbl_derived3`.`tbl_derived2_id` " | |||
"WHERE " | |||
"`tbl_base`.`tbl_base_id`=UuidToBin('X3d127db6-abb9-11e8-98d0-529269fb1459X')"); | |||
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"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(static_cast<base&>(d2)); | |||
} | |||
TEST(CppHibernateTests, destroy_derived3) | |||
{ | |||
StrictMock<mariadb_mock> mock; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_base`, " | |||
"`tbl_derived2`, " | |||
"`T0`, " | |||
"`T1`, " | |||
"`T2`, " | |||
"`tbl_derived3` " | |||
"FROM " | |||
"`tbl_derived3` " | |||
"LEFT JOIN " | |||
"`tbl_derived2` ON `tbl_derived3`.`tbl_derived2_id`=`tbl_derived2`.`tbl_derived2_id` " | |||
"LEFT JOIN " | |||
"`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` " | |||
"LEFT JOIN " | |||
"`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` " | |||
"WHERE " | |||
"`tbl_derived2`.`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_test3` " | |||
"FROM " | |||
"`tbl_test3` " | |||
"WHERE " | |||
"(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
"(`tbl_derived3_id_test3_vector` IS NULL)"); | |||
expect_query(mock, "DELETE " | |||
"`tbl_test3` " | |||
"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.derived2_id = uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.destroy(static_cast<derived2&>(d3)); | |||
} |
@@ -168,11 +168,11 @@ TEST(CppHibernateTests, init) | |||
" FOREIGN KEY (`tbl_base_id`)\n" | |||
" REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" ON UPDATE CASCADE,\n" | |||
" ADD CONSTRAINT `fk_tbl_derived1_to_tbl_test1_id_test1_data`\n" | |||
" FOREIGN KEY (`tbl_test1_id_test1_data`)\n" | |||
" REFERENCES `test`.`tbl_test1` (`tbl_test1_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION"); | |||
expect_query(mock, "ALTER TABLE `tbl_derived2`\n" | |||
@@ -180,21 +180,21 @@ TEST(CppHibernateTests, init) | |||
" FOREIGN KEY (`tbl_base_id`)\n" | |||
" REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" ON UPDATE CASCADE,\n" | |||
" ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_nullable`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_nullable`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_u`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_ptr_u`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" ADD CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_s`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_ptr_s`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION"); | |||
expect_query(mock, "ALTER TABLE `tbl_derived3`\n" | |||
@@ -202,7 +202,7 @@ TEST(CppHibernateTests, init) | |||
" FOREIGN KEY (`tbl_derived2_id`)\n" | |||
" REFERENCES `test`.`tbl_derived2` (`tbl_derived2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION"); | |||
" ON UPDATE CASCADE"); | |||
expect_query(mock, "COMMIT"); | |||
@@ -153,7 +153,9 @@ TEST(CppHibernateTests, update_derived1) | |||
"`u32_ptr_u`=null, " | |||
"`u32_ptr_s`='X789X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test1` " | |||
"FROM " | |||
"`tbl_test1` " | |||
"WHERE " | |||
"`tbl_test1_id` IN (" | |||
@@ -224,7 +226,9 @@ TEST(CppHibernateTests, update_derived2) | |||
"WHERE " | |||
"`tbl_test2_id`=UuidToBin('X3d1283a6-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -249,7 +253,9 @@ TEST(CppHibernateTests, update_derived2) | |||
"`u16_data`='X22X', " | |||
"`i16_data`='X23X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -261,7 +267,9 @@ TEST(CppHibernateTests, update_derived2) | |||
"`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 " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -328,7 +336,9 @@ TEST(CppHibernateTests, update_derived3) | |||
"WHERE " | |||
"`tbl_base_id`=UuidToBin('X3d1288ce-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -339,7 +349,9 @@ TEST(CppHibernateTests, update_derived3) | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -350,7 +362,9 @@ TEST(CppHibernateTests, update_derived3) | |||
"WHERE " | |||
"`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')" | |||
")"); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test2` " | |||
"FROM " | |||
"`tbl_test2` " | |||
"WHERE " | |||
"`tbl_test2_id` IN (" | |||
@@ -414,7 +428,9 @@ TEST(CppHibernateTests, update_derived3) | |||
"`u64_data`='X112X', " | |||
"`i64_data`='X113X'", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test3` " | |||
"FROM " | |||
"`tbl_test3` " | |||
"WHERE " | |||
"(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
@@ -467,7 +483,9 @@ TEST(CppHibernateTests, update_derived3) | |||
"WHERE " | |||
"`tbl_test3_id`=UuidToBin('X3d129134-abb9-11e8-98d0-529269fb1459X')", | |||
result_affected_rows(1)); | |||
expect_query(mock, "DELETE FROM " | |||
expect_query(mock, "DELETE " | |||
"`tbl_test3` " | |||
"FROM " | |||
"`tbl_test3` " | |||
"WHERE " | |||
"(`tbl_derived3_id_test3_list` IS NULL) AND " | |||
@@ -64,8 +64,8 @@ struct result_data | |||
inline MYSQL_RES* next_result() | |||
{ | |||
static MYSQL_RES* value = reinterpret_cast<MYSQL_RES*>(0x1000); | |||
return ++value; | |||
static size_t value = 0x2000; | |||
return reinterpret_cast<MYSQL_RES*>(value++); | |||
} | |||
inline const result_data::data_type& empty_result_data() | |||