| @@ -20,10 +20,6 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| static constexpr decltype(auto) create_table_argument = ""; | static constexpr decltype(auto) create_table_argument = ""; | ||||
| static constexpr decltype(auto) create_key_query = "SELECT Uuid()"; | static constexpr decltype(auto) create_key_query = "SELECT Uuid()"; | ||||
| static constexpr decltype(auto) convert_to_open = "UuidToBin("; | |||||
| static constexpr decltype(auto) convert_to_close = ")"; | |||||
| static constexpr decltype(auto) convert_from_open = "BinToUuid("; | |||||
| static constexpr decltype(auto) convert_from_close = ")"; | |||||
| static bool is_default(const key_type& key) | static bool is_default(const key_type& key) | ||||
| { return key == key_type(); } | { return key == key_type(); } | ||||
| @@ -37,10 +33,6 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| static constexpr decltype(auto) create_table_argument = "AUTO_INCREMENT"; | static constexpr decltype(auto) create_table_argument = "AUTO_INCREMENT"; | ||||
| static constexpr decltype(auto) create_key_query = ""; | static constexpr decltype(auto) create_key_query = ""; | ||||
| static constexpr decltype(auto) convert_to_open = ""; | |||||
| static constexpr decltype(auto) convert_to_close = ""; | |||||
| static constexpr decltype(auto) convert_from_open = ""; | |||||
| static constexpr decltype(auto) convert_from_close = ""; | |||||
| static bool is_default(const key_type& key) | static bool is_default(const key_type& key) | ||||
| { return key == key_type(); } | { return key == key_type(); } | ||||
| @@ -314,16 +314,16 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| { return utl::to_string(value); } | { return utl::to_string(value); } | ||||
| static constexpr const char* convert_to_open() | static constexpr const char* convert_to_open() | ||||
| { return nullptr; } | |||||
| { return "UuidToBin("; } | |||||
| static constexpr const char* convert_to_close() | static constexpr const char* convert_to_close() | ||||
| { return nullptr; } | |||||
| { return ")"; } | |||||
| static constexpr const char* convert_from_open() | static constexpr const char* convert_from_open() | ||||
| { return nullptr; } | |||||
| { return "BinToUuid("; } | |||||
| static constexpr const char* convert_from_close() | static constexpr const char* convert_from_close() | ||||
| { return nullptr; } | |||||
| { return ")"; } | |||||
| }; | }; | ||||
| template<> | template<> | ||||
| @@ -30,8 +30,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| bool value_is_ordered { false }; // value is stored in a ordered container (vector, list, ...) | bool value_is_ordered { false }; // value is stored in a ordered container (vector, list, ...) | ||||
| bool value_is_auto_incremented { false }; // value is a auto incremented field | bool value_is_auto_incremented { false }; // value is a auto incremented field | ||||
| const table_t* table { nullptr }; // table this field belongs to | |||||
| const table_t* referenced_table { nullptr }; // table that belongs to the value (if exists) | |||||
| table_t* table { nullptr }; // table this field belongs to | |||||
| table_t* referenced_table { nullptr }; // table that belongs to the value (if exists) | |||||
| std::string schema_name; // name of the SQL schema | std::string schema_name; // name of the SQL schema | ||||
| std::string table_name; // name of the SQL table | std::string table_name; // name of the SQL table | ||||
| @@ -162,6 +162,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| using base_type::base_type; | using base_type::base_type; | ||||
| virtual void update () override; | |||||
| virtual bool is_default (const data_context& context) const override; | virtual bool is_default (const data_context& context) const override; | ||||
| virtual std::string generate_value(::cppmariadb::connection& connection) const override; | virtual std::string generate_value(::cppmariadb::connection& connection) const override; | ||||
| }; | }; | ||||
| @@ -64,6 +64,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| /* primary_key_field_t */ | /* primary_key_field_t */ | ||||
| template<typename T_field> | |||||
| void primary_key_field_t<T_field> | |||||
| ::update() | |||||
| { | |||||
| base_type::update(); | |||||
| this->value_is_auto_incremented = key_props::auto_generated::value; | |||||
| } | |||||
| template<typename T_field> | template<typename T_field> | ||||
| bool primary_key_field_t<T_field> | bool primary_key_field_t<T_field> | ||||
| ::is_default(const data_context& context) const | ::is_default(const data_context& context) const | ||||
| @@ -21,9 +21,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| struct table_t | struct table_t | ||||
| { | { | ||||
| public: | public: | ||||
| size_t dataset_id { 0 }; | |||||
| size_t base_dataset_id { 0 }; | |||||
| size_t table_id { 0 }; | |||||
| size_t dataset_id { 0 }; | |||||
| size_t base_dataset_id { 0 }; | |||||
| size_t table_id { 0 }; | |||||
| bool is_used_in_container { false }; | |||||
| std::vector<size_t> derived_dataset_ids; | std::vector<size_t> derived_dataset_ids; | ||||
| std::string table_name; | std::string table_name; | ||||
| @@ -31,10 +32,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| fields_t fields; | fields_t fields; | ||||
| const table_t* base_table { nullptr }; | |||||
| const table_t* base_table { nullptr }; | |||||
| std::vector<const table_t*> derived_tables; | std::vector<const table_t*> derived_tables; | ||||
| const field_t* primary_key_field { nullptr }; | |||||
| const field_t* primary_key_field { nullptr }; | |||||
| std::vector<const field_t*> foreign_key_fields; | std::vector<const field_t*> foreign_key_fields; | ||||
| std::vector<const field_t*> foreign_table_fields; | std::vector<const field_t*> foreign_table_fields; | ||||
| std::vector<const field_t*> foreign_table_one_fields; | std::vector<const field_t*> foreign_table_one_fields; | ||||
| @@ -76,8 +76,7 @@ void field_t::update() | |||||
| case attribute_t::compress: | case attribute_t::compress: | ||||
| ss << "COMPRESS("; | ss << "COMPRESS("; | ||||
| break; | break; | ||||
| case attribute_t::primary_key: | |||||
| ss << "UuidToBin("; | |||||
| default: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -93,9 +92,10 @@ void field_t::update() | |||||
| { | { | ||||
| case attribute_t::hex: | case attribute_t::hex: | ||||
| case attribute_t::compress: | case attribute_t::compress: | ||||
| case attribute_t::primary_key: | |||||
| ss << ')'; | ss << ')'; | ||||
| break; | break; | ||||
| default: | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| convert_to_close = ss.str(); | convert_to_close = ss.str(); | ||||
| @@ -114,8 +114,7 @@ void field_t::update() | |||||
| case attribute_t::compress: | case attribute_t::compress: | ||||
| ss << "UNCOMPRESS("; | ss << "UNCOMPRESS("; | ||||
| break; | break; | ||||
| case attribute_t::primary_key: | |||||
| ss << "BinToUuid("; | |||||
| default: | |||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| @@ -131,9 +130,10 @@ void field_t::update() | |||||
| { | { | ||||
| case attribute_t::hex: | case attribute_t::hex: | ||||
| case attribute_t::compress: | case attribute_t::compress: | ||||
| case attribute_t::primary_key: | |||||
| ss << ')'; | ss << ')'; | ||||
| break; | break; | ||||
| default: | |||||
| break; | |||||
| } | } | ||||
| } | } | ||||
| convert_from_close = ss.str(); | convert_from_close = ss.str(); | ||||
| @@ -259,11 +259,16 @@ throw_not_implemented(::cppmariadb::statement&, get_statement_foreign_many_updat | |||||
| << table_name | << table_name | ||||
| << "_id_" | << "_id_" | ||||
| << field_name | << field_name | ||||
| << "`=NULL, `" | |||||
| << table_name | |||||
| << "_index_" | |||||
| << field_name | |||||
| << "`=0 WHERE `" | |||||
| << "`=NULL"; | |||||
| if (value_is_container) | |||||
| { | |||||
| os << ", `" | |||||
| << table_name | |||||
| << "_index_" | |||||
| << field_name | |||||
| << "`=0"; | |||||
| } | |||||
| os << " WHERE `" | |||||
| << table_name | << table_name | ||||
| << "_id_" | << "_id_" | ||||
| << field_name | << field_name | ||||
| @@ -25,6 +25,7 @@ void schema_t::update() | |||||
| table.foreign_table_one_fields.clear(); | table.foreign_table_one_fields.clear(); | ||||
| table.foreign_table_many_fields.clear(); | table.foreign_table_many_fields.clear(); | ||||
| table.data_fields.clear(); | table.data_fields.clear(); | ||||
| table.is_used_in_container = false; | |||||
| for (auto& ptr : table.fields) | for (auto& ptr : table.fields) | ||||
| { | { | ||||
| @@ -58,6 +59,7 @@ void schema_t::update() | |||||
| // update fields | // update fields | ||||
| for (auto& ptr : table.fields) | for (auto& ptr : table.fields) | ||||
| { | { | ||||
| assert(ptr); | |||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| // table | // table | ||||
| @@ -86,12 +88,7 @@ void schema_t::update() | |||||
| table.foreign_table_fields.emplace_back(&field); | table.foreign_table_fields.emplace_back(&field); | ||||
| if (field.value_is_container) | if (field.value_is_container) | ||||
| { | { | ||||
| table.foreign_table_many_fields.emplace_back(&field); | |||||
| referenced_table->foreign_key_fields.push_back(&field); | |||||
| } | |||||
| else | |||||
| { | |||||
| table.foreign_table_one_fields.emplace_back(&field); | |||||
| referenced_table->is_used_in_container = true; | |||||
| } | } | ||||
| } | } | ||||
| @@ -104,6 +101,35 @@ void schema_t::update() | |||||
| if (!static_cast<bool>(table.primary_key_field)) | if (!static_cast<bool>(table.primary_key_field)) | ||||
| throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' does not have a primary key!"); | throw misc::hibernate_exception(std::string("Table '") + table.table_name + "' does not have a primary key!"); | ||||
| } | } | ||||
| // update foreign fields (one, many, key) | |||||
| for (auto& kvp : tables) | |||||
| { | |||||
| assert(static_cast<bool>(kvp.second)); | |||||
| auto& table = *kvp.second; | |||||
| for (auto& ptr : table.foreign_table_fields) | |||||
| { | |||||
| assert(ptr); | |||||
| assert(ptr->referenced_table); | |||||
| auto& field = *ptr; | |||||
| auto& referenced_table = *field.referenced_table; | |||||
| if (field.value_is_container) | |||||
| { | |||||
| table.foreign_table_many_fields.emplace_back(&field); | |||||
| referenced_table.foreign_key_fields.push_back(&field); | |||||
| } | |||||
| else | |||||
| { | |||||
| table.foreign_table_one_fields.emplace_back(&field); | |||||
| if (referenced_table.is_used_in_container) | |||||
| { | |||||
| referenced_table.foreign_key_fields.push_back(&field); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| } | } | ||||
| void schema_t::print(std::ostream& os) const | void schema_t::print(std::ostream& os) const | ||||
| @@ -328,10 +328,14 @@ struct select_query_builder_t | |||||
| for (auto& ptr : ctx.table.foreign_table_one_fields) | for (auto& ptr : ctx.table.foreign_table_one_fields) | ||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| assert(ptr->table); | |||||
| assert(ptr->table->primary_key_field); | |||||
| assert(ptr->referenced_table); | assert(ptr->referenced_table); | ||||
| assert(ptr->referenced_table->primary_key_field); | assert(ptr->referenced_table->primary_key_field); | ||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| auto& table = *field.table; | |||||
| auto& own_key = *table.primary_key_field; | |||||
| auto& ref_table = *field.referenced_table; | auto& ref_table = *field.referenced_table; | ||||
| auto& ref_key = *ref_table.primary_key_field; | auto& ref_key = *ref_table.primary_key_field; | ||||
| @@ -342,21 +346,42 @@ struct select_query_builder_t | |||||
| auto new_alias = make_alias(); | auto new_alias = make_alias(); | ||||
| std::ostringstream ss; | std::ostringstream ss; | ||||
| ss << " 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 | |||||
| << "`"; | |||||
| if (ref_table.is_used_in_container) | |||||
| { | |||||
| ss << " LEFT JOIN `" | |||||
| << ref_table.table_name | |||||
| << "` AS `" | |||||
| << new_alias | |||||
| << "` ON `" | |||||
| << real_alias | |||||
| << "`.`" | |||||
| << own_key.field_name | |||||
| << "`=`" | |||||
| << new_alias | |||||
| << "`.`" | |||||
| << table.table_name | |||||
| << "_id_" | |||||
| << field.field_name | |||||
| << "`"; | |||||
| } | |||||
| else | |||||
| { | |||||
| ss << " 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 | |||||
| << "`"; | |||||
| } | |||||
| auto it = joins.insert(joins.end(), ss.str()); | auto it = joins.insert(joins.end(), ss.str()); | ||||
| if (!add_table({ | if (!add_table({ | ||||
| @@ -498,29 +523,53 @@ struct delete_query_builder_t | |||||
| for (auto& ptr : table.foreign_table_one_fields) | for (auto& ptr : table.foreign_table_one_fields) | ||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| assert(ptr->table); | |||||
| assert(ptr->table->primary_key_field); | |||||
| assert(ptr->referenced_table); | assert(ptr->referenced_table); | ||||
| assert(ptr->referenced_table->primary_key_field); | assert(ptr->referenced_table->primary_key_field); | ||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| auto& own_key = *table.primary_key_field; | |||||
| auto& ref_table = *field.referenced_table; | auto& ref_table = *field.referenced_table; | ||||
| auto& ref_key = *ref_table.primary_key_field; | auto& ref_key = *ref_table.primary_key_field; | ||||
| auto new_alias = make_alias(); | |||||
| 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 | |||||
| << "`"; | |||||
| if (ref_table.is_used_in_container) | |||||
| { | |||||
| os << " LEFT JOIN `" | |||||
| << ref_table.table_name | |||||
| << "` AS `" | |||||
| << new_alias | |||||
| << "` ON `" | |||||
| << real_alias | |||||
| << "`.`" | |||||
| << own_key.field_name | |||||
| << "`=`" | |||||
| << new_alias | |||||
| << "`.`" | |||||
| << table.table_name | |||||
| << "_id_" | |||||
| << field.field_name | |||||
| << "`"; | |||||
| } | |||||
| else | |||||
| { | |||||
| 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); | add_table(ref_table, new_alias, true, true); | ||||
| } | } | ||||
| @@ -645,6 +694,8 @@ std::string build_init_stage1_query(const table_t& table) | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| assert(field_info.referenced_table); | assert(field_info.referenced_table); | ||||
| assert(field_info.referenced_table->primary_key_field); | assert(field_info.referenced_table->primary_key_field); | ||||
| if (field_info.referenced_table->is_used_in_container) | |||||
| continue; | |||||
| auto& ref_key_info = *field_info.referenced_table->primary_key_field; | auto& ref_key_info = *field_info.referenced_table->primary_key_field; | ||||
| os << indent | os << indent | ||||
| << "`" | << "`" | ||||
| @@ -753,6 +804,8 @@ std::string build_init_stage1_query(const table_t& table) | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| assert(field_info.referenced_table); | assert(field_info.referenced_table); | ||||
| assert(field_info.referenced_table->primary_key_field); | assert(field_info.referenced_table->primary_key_field); | ||||
| if (field_info.referenced_table->is_used_in_container) | |||||
| continue; | |||||
| auto& ref_key_info = *field_info.referenced_table->primary_key_field; | auto& ref_key_info = *field_info.referenced_table->primary_key_field; | ||||
| os << "," | os << "," | ||||
| << indent | << indent | ||||
| @@ -848,6 +901,8 @@ std::string build_init_stage2_query(const table_t& table) | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| assert(field_info.referenced_table); | assert(field_info.referenced_table); | ||||
| assert(field_info.referenced_table->primary_key_field); | assert(field_info.referenced_table->primary_key_field); | ||||
| if (field_info.referenced_table->is_used_in_container) | |||||
| continue; | |||||
| auto& ref_key_info = *field_info.referenced_table->primary_key_field; | auto& ref_key_info = *field_info.referenced_table->primary_key_field; | ||||
| if (index++) os << ","; | if (index++) os << ","; | ||||
| os << indent | os << indent | ||||
| @@ -940,7 +995,7 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| : "INSERT INTO") | : "INSERT INTO") | ||||
| << " `" | << " `" | ||||
| << table.table_name | << table.table_name | ||||
| << "` SET "; | |||||
| << "`"; | |||||
| /* primary key */ | /* primary key */ | ||||
| if (!is_update) | if (!is_update) | ||||
| @@ -951,6 +1006,8 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| { | { | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| else | |||||
| os << " SET "; | |||||
| os << "`" | os << "`" | ||||
| << key_info.field_name | << key_info.field_name | ||||
| << "`=" | << "`=" | ||||
| @@ -969,6 +1026,8 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| { | { | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| else | |||||
| os << " SET "; | |||||
| auto& base_table_info = *table.base_table; | auto& base_table_info = *table.base_table; | ||||
| assert(base_table_info.primary_key_field); | assert(base_table_info.primary_key_field); | ||||
| auto& key_info = *base_table_info.primary_key_field; | auto& key_info = *base_table_info.primary_key_field; | ||||
| @@ -989,10 +1048,14 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| if (is_update && filter->is_excluded(field_info)) | if (is_update && filter->is_excluded(field_info)) | ||||
| continue; | continue; | ||||
| if (index++) | |||||
| os << ", "; | |||||
| assert(field_info.referenced_table); | assert(field_info.referenced_table); | ||||
| assert(field_info.referenced_table->primary_key_field); | assert(field_info.referenced_table->primary_key_field); | ||||
| if (field_info.referenced_table->is_used_in_container) | |||||
| continue; | |||||
| if (index++) | |||||
| os << ", "; | |||||
| else | |||||
| os << " SET "; | |||||
| auto& key_info = *field_info.referenced_table->primary_key_field; | auto& key_info = *field_info.referenced_table->primary_key_field; | ||||
| os << "`" | os << "`" | ||||
| << key_info.table_name | << key_info.table_name | ||||
| @@ -1016,6 +1079,8 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| continue; | continue; | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| else | |||||
| os << " SET "; | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| assert(field_info.table); | assert(field_info.table); | ||||
| assert(field_info.table->primary_key_field); | assert(field_info.table->primary_key_field); | ||||
| @@ -1035,7 +1100,9 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| if (field_info.value_is_ordered) | if (field_info.value_is_ordered) | ||||
| { | { | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | |||||
| os << ", "; | |||||
| else | |||||
| os << " SET "; | |||||
| os << "`" | os << "`" | ||||
| << field_info.table_name | << field_info.table_name | ||||
| << "_index_" | << "_index_" | ||||
| @@ -1053,6 +1120,8 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| continue; | continue; | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| else | |||||
| os << " SET "; | |||||
| os << "`" | os << "`" | ||||
| << field_info.field_name | << field_info.field_name | ||||
| << "`=" | << "`=" | ||||
| @@ -1070,6 +1139,8 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| { | { | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| else | |||||
| os << " SET "; | |||||
| os << "`__type`=?__type?"; | os << "`__type`=?__type?"; | ||||
| } | } | ||||
| @@ -1088,7 +1159,7 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| << key_info.convert_to_close; | << key_info.convert_to_close; | ||||
| } | } | ||||
| return index == 0 | |||||
| return index == 0 && !(table.primary_key_field->value_is_auto_incremented && !is_update) | |||||
| ? std::string() | ? std::string() | ||||
| : os.str(); | : os.str(); | ||||
| } | } | ||||
| @@ -1118,17 +1189,16 @@ std::string table_t::execute_create_update( | |||||
| /* primary key */ | /* primary key */ | ||||
| assert(primary_key_field); | assert(primary_key_field); | ||||
| if ( !primary_key_field->value_is_auto_incremented | |||||
| && !is_update) | |||||
| if (is_update) | |||||
| { | |||||
| primary_key = get_primary_key(context); | |||||
| } | |||||
| else if (!primary_key_field->value_is_auto_incremented) | |||||
| { | { | ||||
| primary_key = primary_key_field->generate_value(context.connection); | primary_key = primary_key_field->generate_value(context.connection); | ||||
| if (statement) statement->set(index, primary_key); | if (statement) statement->set(index, primary_key); | ||||
| ++index; | ++index; | ||||
| } | } | ||||
| else | |||||
| { | |||||
| primary_key = get_primary_key(context); | |||||
| } | |||||
| /* base_key */ | /* base_key */ | ||||
| if ( base_table | if ( base_table | ||||
| @@ -1150,9 +1220,13 @@ std::string table_t::execute_create_update( | |||||
| for (auto& ptr : foreign_table_one_fields) | for (auto& ptr : foreign_table_one_fields) | ||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| assert(ptr->referenced_table); | |||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| if (is_update && filter.is_excluded(field)) | if (is_update && filter.is_excluded(field)) | ||||
| continue; | continue; | ||||
| if (field.referenced_table->is_used_in_container) | |||||
| continue; | |||||
| /* insert/update dataset */ | /* insert/update dataset */ | ||||
| value_t key = field.foreign_create_update(context); | value_t key = field.foreign_create_update(context); | ||||
| @@ -1288,7 +1362,7 @@ std::string table_t::execute_create_update( | |||||
| } | } | ||||
| /* foreign table many fields */ | /* foreign table many fields */ | ||||
| for (auto& ptr : foreign_table_many_fields) | |||||
| for (auto& ptr : foreign_table_fields) | |||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| assert(ptr->referenced_table); | assert(ptr->referenced_table); | ||||
| @@ -1296,6 +1370,9 @@ std::string table_t::execute_create_update( | |||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| auto& ref_table = *field.referenced_table; | auto& ref_table = *field.referenced_table; | ||||
| if (!ref_table.is_used_in_container) | |||||
| continue; | |||||
| if ( is_update | if ( is_update | ||||
| && ( filter.is_excluded(field) | && ( filter.is_excluded(field) | ||||
| || filter.is_excluded(ref_table))) | || filter.is_excluded(ref_table))) | ||||
| @@ -1558,7 +1635,8 @@ void table_t::execute_foreign_many_delete(const base_context& context) const | |||||
| std::string table_t::get_primary_key(const data_context& context) const | std::string table_t::get_primary_key(const data_context& context) const | ||||
| { | { | ||||
| assert(primary_key_field); | assert(primary_key_field); | ||||
| if (primary_key_field->is_default(context)) | |||||
| if ( primary_key_field->is_default(context) | |||||
| && base_table) | |||||
| { | { | ||||
| auto key = get_key_from_base(context); | auto key = get_key_from_base(context); | ||||
| primary_key_field->set(context, key); | primary_key_field->set(context, key); | ||||
| @@ -1573,7 +1651,9 @@ std::string table_t::get_primary_key(const data_context& context) const | |||||
| std::string table_t::get_key_from_base(const data_context& context) const | std::string table_t::get_key_from_base(const data_context& context) const | ||||
| { | { | ||||
| if (!base_table) | if (!base_table) | ||||
| { | |||||
| throw exception(std::string("table has no base table: ") + table_name); | throw exception(std::string("table has no base table: ") + table_name); | ||||
| } | |||||
| auto& statement = get_statement_key_from_base(); | auto& statement = get_statement_key_from_base(); | ||||
| auto base_key = base_table->get_primary_key(context); | auto base_key = base_table->get_primary_key(context); | ||||
| statement.set(0, base_key); | statement.set(0, base_key); | ||||
| @@ -517,4 +517,63 @@ TEST(CppHibernateTests, create_dummy_owner) | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| context.create(d); | context.create(d); | ||||
| } | |||||
| TEST(CppHibernateTests, create_double_usage) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "INSERT INTO `tbl_double_usage`", | |||||
| result_id(1)); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_single_item`='X1X', " | |||||
| "`tbl_double_usage_id_multiple_items`=null, " | |||||
| "`tbl_double_usage_index_multiple_items`='X0X', " | |||||
| "`data`='X123X'", | |||||
| result_id(1001)); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_single_item`=null, " | |||||
| "`tbl_double_usage_id_multiple_items`='X1X', " | |||||
| "`tbl_double_usage_index_multiple_items`='X0X', " | |||||
| "`data`='X456X'", | |||||
| result_id(1002)); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_single_item`=null, " | |||||
| "`tbl_double_usage_id_multiple_items`='X1X', " | |||||
| "`tbl_double_usage_index_multiple_items`='X1X', " | |||||
| "`data`='X789X'", | |||||
| result_id(1003)); | |||||
| 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))); | |||||
| double_usage d; | |||||
| d.single_item.reset(new double_usage_item(123)); | |||||
| d.multiple_items.emplace_back(456); | |||||
| d.multiple_items.emplace_back(789); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.create(d); | |||||
| EXPECT_EQ(d.id, 1); | |||||
| EXPECT_EQ(d.single_item->id, 1001); | |||||
| EXPECT_EQ(d.multiple_items[0].id, 1002); | |||||
| EXPECT_EQ(d.multiple_items[1].id, 1003); | |||||
| } | } | ||||
| @@ -247,4 +247,46 @@ TEST(CppHibernateTests, destroy_derived3) | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| context.destroy(static_cast<derived2&>(d3)); | context.destroy(static_cast<derived2&>(d3)); | ||||
| } | |||||
| TEST(CppHibernateTests, destroy_double_usage) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "DELETE " | |||||
| "`tbl_double_usage`, " | |||||
| "`T0` " | |||||
| "FROM " | |||||
| "`tbl_double_usage` " | |||||
| "LEFT JOIN " | |||||
| "`tbl_double_usage_item` AS `T0` ON `tbl_double_usage`.`tbl_double_usage_id`=`T0`.`tbl_double_usage_id_single_item` " | |||||
| "WHERE " | |||||
| "`tbl_double_usage`.`tbl_double_usage_id`='X1X'"); | |||||
| expect_query(mock, "DELETE " | |||||
| "`tbl_double_usage_item` " | |||||
| "FROM " | |||||
| "`tbl_double_usage_item` " | |||||
| "WHERE " | |||||
| "(`tbl_double_usage_id_single_item` IS NULL) " | |||||
| "AND (`tbl_double_usage_id_multiple_items` 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))); | |||||
| double_usage d; | |||||
| d.id = 1; | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.destroy(d); | |||||
| } | } | ||||
| @@ -173,6 +173,30 @@ TEST(CppHibernateTests, init) | |||||
| "ENGINE = InnoDB\n" | "ENGINE = InnoDB\n" | ||||
| "DEFAULT CHARACTER SET = utf8"); | "DEFAULT CHARACTER SET = utf8"); | ||||
| expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage_item`\n" | |||||
| "(\n" | |||||
| " `tbl_double_usage_item_id` INT UNSIGNED NOT NULL,\n" | |||||
| " `tbl_double_usage_id_single_item` INT NULL DEFAULT NULL,\n" | |||||
| " `tbl_double_usage_id_multiple_items` INT NULL DEFAULT NULL,\n" | |||||
| " `tbl_double_usage_index_multiple_items` INT UNSIGNED NOT NULL,\n" | |||||
| " `data` INT NOT NULL,\n" | |||||
| " PRIMARY KEY ( `tbl_double_usage_item_id` ),\n" | |||||
| " UNIQUE INDEX `index_tbl_double_usage_item_id` ( `tbl_double_usage_item_id` ASC ),\n" | |||||
| " INDEX `index_tbl_double_usage_id_single_item` ( `tbl_double_usage_id_single_item` ASC ),\n" | |||||
| " INDEX `index_tbl_double_usage_id_multiple_items` ( `tbl_double_usage_id_multiple_items` ASC )\n" | |||||
| ")\n" | |||||
| "ENGINE = InnoDB\n" | |||||
| "DEFAULT CHARACTER SET = utf8"); | |||||
| expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage`\n" | |||||
| "(\n" | |||||
| " `tbl_double_usage_id` INT NOT NULL,\n" | |||||
| " PRIMARY KEY ( `tbl_double_usage_id` ),\n" | |||||
| " UNIQUE INDEX `index_tbl_double_usage_id` ( `tbl_double_usage_id` ASC )\n" | |||||
| ")\n" | |||||
| "ENGINE = InnoDB\n" | |||||
| "DEFAULT CHARACTER SET = utf8"); | |||||
| expect_query(mock, "ALTER TABLE `tbl_test3`\n" | expect_query(mock, "ALTER TABLE `tbl_test3`\n" | ||||
| " ADD CONSTRAINT `fk_tbl_test3_to_tbl_derived3_id_test3_list`\n" | " ADD CONSTRAINT `fk_tbl_test3_to_tbl_derived3_id_test3_list`\n" | ||||
| " FOREIGN KEY IF NOT EXISTS (`tbl_derived3_id_test3_list`)\n" | " FOREIGN KEY IF NOT EXISTS (`tbl_derived3_id_test3_list`)\n" | ||||
| @@ -233,6 +257,18 @@ TEST(CppHibernateTests, init) | |||||
| " ON DELETE SET NULL\n" | " ON DELETE SET NULL\n" | ||||
| " ON UPDATE NO ACTION"); | " ON UPDATE NO ACTION"); | ||||
| expect_query(mock, "ALTER TABLE `tbl_double_usage_item`\n" | |||||
| " ADD CONSTRAINT `fk_tbl_double_usage_item_to_tbl_double_usage_id_single_item`\n" | |||||
| " FOREIGN KEY IF NOT EXISTS (`tbl_double_usage_id_single_item`)\n" | |||||
| " REFERENCES `test`.`tbl_double_usage` (`tbl_double_usage_id`)\n" | |||||
| " ON DELETE SET NULL\n" | |||||
| " ON UPDATE NO ACTION,\n" | |||||
| " ADD CONSTRAINT `fk_tbl_double_usage_item_to_tbl_double_usage_id_multiple_items`\n" | |||||
| " FOREIGN KEY IF NOT EXISTS (`tbl_double_usage_id_multiple_items`)\n" | |||||
| " REFERENCES `test`.`tbl_double_usage` (`tbl_double_usage_id`)\n" | |||||
| " ON DELETE SET NULL\n" | |||||
| " ON UPDATE NO ACTION"); | |||||
| expect_query(mock, "COMMIT"); | expect_query(mock, "COMMIT"); | ||||
| EXPECT_CALL( | EXPECT_CALL( | ||||
| @@ -917,4 +917,67 @@ TEST(CppHibernateTests, read_dummy_owner) | |||||
| EXPECT_EQ(d.dummies[0].data, 123); | EXPECT_EQ(d.dummies[0].data, 123); | ||||
| EXPECT_EQ(d.dummies[1].data, 456); | EXPECT_EQ(d.dummies[1].data, 456); | ||||
| EXPECT_EQ(d.dummies[2].data, 789); | EXPECT_EQ(d.dummies[2].data, 789); | ||||
| } | |||||
| TEST(CppHibernateTests, read_double_usage) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "`tbl_double_usage`.`tbl_double_usage_id`, " | |||||
| "`T0`.`tbl_double_usage_item_id`, " | |||||
| "`T0`.`data` " | |||||
| "FROM " | |||||
| "`tbl_double_usage` " | |||||
| "LEFT JOIN " | |||||
| "`tbl_double_usage_item` AS `T0` ON `tbl_double_usage`.`tbl_double_usage_id`=`T0`.`tbl_double_usage_id_single_item` " | |||||
| "WHERE " | |||||
| "(`tbl_double_usage`.`tbl_double_usage_id`='X1X') ", | |||||
| result_used({ | |||||
| { "1", "1001", "123" } | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "`tbl_double_usage_item`.`tbl_double_usage_item_id`, " | |||||
| "`tbl_double_usage_item`.`data` " | |||||
| "FROM " | |||||
| "`tbl_double_usage_item` " | |||||
| "WHERE " | |||||
| "(`tbl_double_usage_item`.`tbl_double_usage_id_multiple_items`='X1X') " | |||||
| "ORDER BY " | |||||
| "`tbl_double_usage_item`.`tbl_double_usage_index_multiple_items` ASC ", | |||||
| result_used({ | |||||
| { "1002", "456" }, | |||||
| { "1003", "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())); | |||||
| double_usage d; | |||||
| d.id = 1; | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.read(d); | |||||
| EXPECT_EQ (d.id, 1); | |||||
| ASSERT_TRUE(d.single_item); | |||||
| ASSERT_EQ (d.multiple_items.size(), 2); | |||||
| EXPECT_EQ (d.single_item->id, 1001); | |||||
| EXPECT_EQ (d.single_item->data, 123); | |||||
| EXPECT_EQ (d.multiple_items[0].id, 1002); | |||||
| EXPECT_EQ (d.multiple_items[0].data, 456); | |||||
| EXPECT_EQ (d.multiple_items[1].id, 1003); | |||||
| EXPECT_EQ (d.multiple_items[1].data, 789); | |||||
| } | } | ||||
| @@ -706,6 +706,95 @@ TEST(CppHibernateTests, update_dummy_owner) | |||||
| d.dummies.emplace_back(456); | d.dummies.emplace_back(456); | ||||
| d.dummies.emplace_back(789); | d.dummies.emplace_back(789); | ||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.update(d); | |||||
| } | |||||
| TEST(CppHibernateTests, update_double_usage) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_single_item`=NULL " | |||||
| "WHERE " | |||||
| "`tbl_double_usage_id_single_item`='X1X'"); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_single_item`='X1X', " | |||||
| "`data`='X123X' " | |||||
| "WHERE " | |||||
| "`tbl_double_usage_item_id`='X1001X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "DELETE " | |||||
| "`tbl_double_usage_item` " | |||||
| "FROM " | |||||
| "`tbl_double_usage_item` " | |||||
| "WHERE " | |||||
| "(`tbl_double_usage_id_single_item` IS NULL) " | |||||
| "AND (`tbl_double_usage_id_multiple_items` IS NULL)"); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_multiple_items`=NULL, " | |||||
| "`tbl_double_usage_index_multiple_items`=0 " | |||||
| "WHERE " | |||||
| "`tbl_double_usage_id_multiple_items`='X1X'"); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_multiple_items`='X1X', " | |||||
| "`tbl_double_usage_index_multiple_items`='X0X', " | |||||
| "`data`='X456X' " | |||||
| "WHERE " | |||||
| "`tbl_double_usage_item_id`='X1002X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_double_usage_item` " | |||||
| "SET " | |||||
| "`tbl_double_usage_id_multiple_items`='X1X', " | |||||
| "`tbl_double_usage_index_multiple_items`='X1X', " | |||||
| "`data`='X789X' " | |||||
| "WHERE " | |||||
| "`tbl_double_usage_item_id`='X1003X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "DELETE " | |||||
| "`tbl_double_usage_item` " | |||||
| "FROM " | |||||
| "`tbl_double_usage_item` " | |||||
| "WHERE " | |||||
| "(`tbl_double_usage_id_single_item` IS NULL) " | |||||
| "AND (`tbl_double_usage_id_multiple_items` 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))); | |||||
| double_usage d; | |||||
| d.id = 1; | |||||
| d.single_item.reset(new double_usage_item()); | |||||
| d.single_item->id = 1001; | |||||
| d.single_item->data = 123; | |||||
| d.multiple_items.emplace_back(); | |||||
| d.multiple_items.back().id = 1002; | |||||
| d.multiple_items.back().data = 456; | |||||
| d.multiple_items.emplace_back(); | |||||
| d.multiple_items.back().id = 1003; | |||||
| d.multiple_items.back().data = 789; | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| context.update(d); | context.update(d); | ||||
| @@ -38,14 +38,16 @@ struct result_data | |||||
| bool is_stored; | bool is_stored; | ||||
| ssize_t affected_rows; | ssize_t affected_rows; | ||||
| ssize_t return_id; | |||||
| data_type data; | data_type data; | ||||
| interal_data_vector internal_data; | interal_data_vector internal_data; | ||||
| template<typename T_data> | template<typename T_data> | ||||
| result_data(T_data&& p_data, bool p_is_stored, ssize_t p_affected_rows) | |||||
| result_data(T_data&& p_data, bool p_is_stored, ssize_t p_affected_rows, ssize_t p_return_id) | |||||
| : data (std::forward<T_data>(p_data)) | : data (std::forward<T_data>(p_data)) | ||||
| , is_stored (p_is_stored) | , is_stored (p_is_stored) | ||||
| , affected_rows (p_affected_rows) | , affected_rows (p_affected_rows) | ||||
| , return_id (p_return_id) | |||||
| { | { | ||||
| internal_data.resize(data.size()); | internal_data.resize(data.size()); | ||||
| for (size_t i = 0; i < data.size(); ++i) | for (size_t i = 0; i < data.size(); ++i) | ||||
| @@ -78,14 +80,17 @@ inline const result_data::data_type& empty_result_data() | |||||
| template<typename T_data = decltype(empty_result_data())> | template<typename T_data = decltype(empty_result_data())> | ||||
| inline decltype(auto) result_stored(T_data&& data = empty_result_data(), ssize_t affected_rows = -1) | inline decltype(auto) result_stored(T_data&& data = empty_result_data(), ssize_t affected_rows = -1) | ||||
| { return result_data(std::forward<T_data>(data), true, affected_rows); } | |||||
| { return result_data(std::forward<T_data>(data), true, affected_rows, -1); } | |||||
| template<typename T_data = decltype(empty_result_data())> | template<typename T_data = decltype(empty_result_data())> | ||||
| inline decltype(auto) result_used(T_data&& data = empty_result_data(), ssize_t affected_rows = -1) | inline decltype(auto) result_used(T_data&& data = empty_result_data(), ssize_t affected_rows = -1) | ||||
| { return result_data(std::forward<T_data>(data), false, affected_rows); } | |||||
| { return result_data(std::forward<T_data>(data), false, affected_rows, -1); } | |||||
| inline decltype(auto) result_affected_rows(ssize_t affected_rows) | inline decltype(auto) result_affected_rows(ssize_t affected_rows) | ||||
| { return result_data(empty_result_data(), true, affected_rows); } | |||||
| { return result_data(empty_result_data(), true, affected_rows, -1); } | |||||
| inline decltype(auto) result_id(ssize_t id) | |||||
| { return result_data(empty_result_data(), true, -1, id); } | |||||
| template<typename T_mock, typename T_result> | template<typename T_mock, typename T_result> | ||||
| inline void expect_query(T_mock& mock, const std::string& query, T_result&& result) | inline void expect_query(T_mock& mock, const std::string& query, T_result&& result) | ||||
| @@ -126,6 +131,14 @@ inline void expect_query(T_mock& mock, const std::string& query, T_result&& resu | |||||
| .InSequence(mock.sequence) | .InSequence(mock.sequence) | ||||
| .WillOnce(::testing::Return(static_cast<unsigned long long>(res.affected_rows))); | .WillOnce(::testing::Return(static_cast<unsigned long long>(res.affected_rows))); | ||||
| } | } | ||||
| else if (res.return_id >= 0) | |||||
| { | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_insert_id(reinterpret_cast<MYSQL*>(0x1111))) | |||||
| .InSequence(mock.sequence) | |||||
| .WillOnce(::testing::Return(static_cast<unsigned long long>(res.return_id))); | |||||
| } | |||||
| if (!res.data.empty()) | if (!res.data.empty()) | ||||
| { | { | ||||
| @@ -108,6 +108,23 @@ struct dummy_owner | |||||
| std::vector<dummy_id> dummies; | std::vector<dummy_id> dummies; | ||||
| }; | }; | ||||
| struct double_usage_item | |||||
| { | |||||
| unsigned int id { 0 }; | |||||
| int data { 0 }; | |||||
| double_usage_item(int p_data = 0) | |||||
| : data(p_data) | |||||
| { } | |||||
| }; | |||||
| struct double_usage | |||||
| { | |||||
| int id { 0 }; | |||||
| std::unique_ptr<double_usage_item> single_item; | |||||
| std::vector<double_usage_item> multiple_items; | |||||
| }; | |||||
| constexpr decltype(auto) test_schema = cpphibernate_make_schema( | constexpr decltype(auto) test_schema = cpphibernate_make_schema( | ||||
| test, | test, | ||||
| cpphibernate_make_table_name( | cpphibernate_make_table_name( | ||||
| @@ -187,5 +204,20 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||||
| 15, | 15, | ||||
| cpphibernate_make_id (&dummy_owner::id), | cpphibernate_make_id (&dummy_owner::id), | ||||
| cpphibernate_make_field (dummy_owner, dummies) | cpphibernate_make_field (dummy_owner, dummies) | ||||
| ), | |||||
| cpphibernate_make_table_name( | |||||
| tbl_double_usage_item, | |||||
| double_usage_item, | |||||
| 16, | |||||
| cpphibernate_make_id (&double_usage_item::id), | |||||
| cpphibernate_make_field (double_usage_item, data) | |||||
| ), | |||||
| cpphibernate_make_table_name( | |||||
| tbl_double_usage, | |||||
| double_usage, | |||||
| 17, | |||||
| cpphibernate_make_id (&double_usage::id), | |||||
| cpphibernate_make_field (double_usage, single_item), | |||||
| cpphibernate_make_field (double_usage, multiple_items) | |||||
| ) | ) | ||||
| ); | ); | ||||