| @@ -111,9 +111,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| ::cppmariadb::statement& get_statement_key_from_base() const; | ::cppmariadb::statement& get_statement_key_from_base() const; | ||||
| ::cppmariadb::statement& get_statement_create_table() const; | ::cppmariadb::statement& get_statement_create_table() const; | ||||
| ::cppmariadb::statement* get_statement_alter_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_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_foreign_many_delete() const; | ||||
| ::cppmariadb::statement& get_statement_delete() const; | ::cppmariadb::statement& get_statement_delete() const; | ||||
| @@ -121,7 +121,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| std::string execute_create_update( | std::string execute_create_update( | ||||
| const create_update_context& context, | 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_primary_key(const data_context& context) const; | ||||
| std::string get_key_from_base(const data_context& context) const; | std::string get_key_from_base(const data_context& context) const; | ||||
| @@ -72,11 +72,11 @@ beg_namespace_cpphibernate_schema | |||||
| /* getter_lambda_t */ | /* getter_lambda_t */ | ||||
| template<typename T_dataset, typename T_lambda> | |||||
| template<typename T_lambda, typename T_dataset, typename T_value> | |||||
| struct getter_lambda_t | 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 dataset_type = typename base_type::dataset_type; | ||||
| using value_type = typename base_type::value_type; | using value_type = typename base_type::value_type; | ||||
| using lambda_type = T_lambda; | using lambda_type = T_lambda; | ||||
| @@ -104,8 +104,8 @@ beg_namespace_cpphibernate_schema | |||||
| { }; | { }; | ||||
| template<typename T> | 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 | : public mp::c_true_t | ||||
| { }; | { }; | ||||
| @@ -122,19 +122,31 @@ beg_namespace_cpphibernate_schema | |||||
| template<typename T_dataset, typename T_value> | template<typename T_dataset, typename T_value> | ||||
| constexpr decltype(auto) make_getter_member_var(T_value T_dataset::* member) | 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> | template<typename T_dataset, typename T_value> | ||||
| constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)()) | 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> | template<typename T_dataset, typename T_value> | ||||
| constexpr decltype(auto) make_getter_member_func(T_value (T_dataset::*member)() const) | 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 */ | /* operations */ | ||||
| @@ -160,6 +172,11 @@ beg_namespace_cpphibernate_schema | |||||
| template<typename T_func, typename T_dataset, typename T_value> | 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 | 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)); } | { return make_getter_lambda(std::forward<T_func>(func), std::forward<hana::type<T_dataset>>(wrapped_dataset), std::forward<hana::type<T_value>>(wrapped_value)); } | ||||
| template<typename T_getter> | |||||
| constexpr auto operator()(T_getter&& getter) const | |||||
| -> mp::enable_if<is_getter<T_getter>, T_getter> | |||||
| { return std::forward<T_getter>(getter); } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -41,3 +41,18 @@ | |||||
| id, \ | id, \ | ||||
| p_member_ptr, \ | p_member_ptr, \ | ||||
| cpphibernate::schema::attribute::primary_key) | cpphibernate::schema::attribute::primary_key) | ||||
| #define cpphibernate_make_temp_id(p_dataset, p_value) \ | |||||
| cpphibernate_make_field_custom( \ | |||||
| id, \ | |||||
| ::cpphibernate::schema::make_getter_lambda( \ | |||||
| [](p_dataset&) { \ | |||||
| return p_value { }; \ | |||||
| }, \ | |||||
| ::boost::hana::type_c<p_dataset>, \ | |||||
| ::boost::hana::type_c<p_value>), \ | |||||
| ::cpphibernate::schema::make_setter_lambda( \ | |||||
| [](p_dataset&, p_value&&) { }, \ | |||||
| ::boost::hana::type_c<p_dataset>, \ | |||||
| ::boost::hana::type_c<p_value>), \ | |||||
| cpphibernate::schema::attribute::primary_key) | |||||
| @@ -11,20 +11,21 @@ beg_namespace_cpphibernate_schema | |||||
| /* setter_t */ | /* setter_t */ | ||||
| template<typename T_value> | |||||
| template<typename T_dataset, typename T_value> | |||||
| struct setter_t | struct setter_t | ||||
| { | { | ||||
| using value_type = T_value; | |||||
| using dataset_type = T_dataset; | |||||
| using value_type = T_value; | |||||
| }; | }; | ||||
| /* setter_none_t */ | /* setter_none_t */ | ||||
| struct 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_constructable(setter_none_t, default); | ||||
| cpphibernate_copyable (setter_none_t, delete); | cpphibernate_copyable (setter_none_t, delete); | ||||
| @@ -35,11 +36,11 @@ beg_namespace_cpphibernate_schema | |||||
| template<typename T_dataset, typename T_value, typename T_member> | template<typename T_dataset, typename T_value, typename T_member> | ||||
| struct setter_member_var_t | 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 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; | using member_type = T_member; | ||||
| member_type member; | member_type member; | ||||
| @@ -61,11 +62,11 @@ beg_namespace_cpphibernate_schema | |||||
| template<typename T_dataset, typename T_value, typename T_member> | template<typename T_dataset, typename T_value, typename T_member> | ||||
| struct setter_member_func_t | 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 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; | using member_type = T_member; | ||||
| member_type member; | member_type member; | ||||
| @@ -85,13 +86,13 @@ beg_namespace_cpphibernate_schema | |||||
| /* setter_lambda_t */ | /* 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 | 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 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; | using lambda_type = T_lambda; | ||||
| lambda_type lambda; | lambda_type lambda; | ||||
| @@ -117,8 +118,8 @@ beg_namespace_cpphibernate_schema | |||||
| { }; | { }; | ||||
| template<typename T> | 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 | : public mp::c_true_t | ||||
| { }; | { }; | ||||
| @@ -134,23 +135,38 @@ beg_namespace_cpphibernate_schema | |||||
| /* make */ | /* make */ | ||||
| constexpr decltype(auto) make_setter_none() | 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> | template<typename T_dataset, typename T_value> | ||||
| constexpr decltype(auto) make_setter_member_var(T_value T_dataset::* member) | 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> | template<typename T_dataset, typename T_value, typename T_return> | ||||
| constexpr decltype(auto) make_setter_member_func(T_return (T_dataset::*member)(T_value)) | 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> | 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) | 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 */ | /* operations */ | ||||
| @@ -177,9 +193,14 @@ beg_namespace_cpphibernate_schema | |||||
| constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value&&)) const | constexpr decltype(auto) operator()(void (T_dataset::*member)(T_value&&)) const | ||||
| { return make_setter_member_func(member); } | { return make_setter_member_func(member); } | ||||
| template<typename T_func, typename T_dataset, typename T_value> | |||||
| constexpr decltype(auto) operator()(T_func&& func, hana::type<T_dataset>&& wrapped_dataset, hana::type<T_value>&& wrapped_value) const | |||||
| { return make_setter_lambda(std::forward<T_func>(func), std::forward<hana::type<T_dataset>>(wrapped_dataset), std::forward<hana::type<T_value>>(wrapped_value)); } | |||||
| template<typename T_func, typename T_wrapped_dataset, typename T_wrapped_value> | |||||
| constexpr decltype(auto) operator()(T_func&& func, T_wrapped_dataset&&, T_wrapped_value&&) const | |||||
| { return make_setter_lambda(std::forward<T_func>(func), T_wrapped_dataset { }, T_wrapped_value { }); } | |||||
| template<typename T_setter> | |||||
| constexpr auto operator()(T_setter&& setter) const | |||||
| -> mp::enable_if<is_setter<T_setter>, T_setter> | |||||
| { return std::forward<T_setter>(setter); } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -1088,7 +1088,9 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| << key_info.convert_to_close; | << key_info.convert_to_close; | ||||
| } | } | ||||
| return os.str(); | |||||
| return index == 0 | |||||
| ? std::string() | |||||
| : os.str(); | |||||
| } | } | ||||
| std::string build_select_query( | std::string build_select_query( | ||||
| @@ -1103,7 +1105,7 @@ std::string build_select_query( | |||||
| std::string table_t::execute_create_update( | std::string table_t::execute_create_update( | ||||
| const create_update_context& context, | const create_update_context& context, | ||||
| ::cppmariadb::statement& statement) const | |||||
| ::cppmariadb::statement* statement) const | |||||
| { | { | ||||
| auto& connection = context.connection; | auto& connection = context.connection; | ||||
| auto& filter = context.filter; | auto& filter = context.filter; | ||||
| @@ -1112,7 +1114,7 @@ std::string table_t::execute_create_update( | |||||
| bool is_update = context.is_update; | bool is_update = context.is_update; | ||||
| std::string primary_key; | std::string primary_key; | ||||
| statement.clear(); | |||||
| if (statement) statement->clear(); | |||||
| /* primary key */ | /* primary key */ | ||||
| assert(primary_key_field); | assert(primary_key_field); | ||||
| @@ -1120,7 +1122,7 @@ std::string table_t::execute_create_update( | |||||
| && !is_update) | && !is_update) | ||||
| { | { | ||||
| primary_key = primary_key_field->generate_value(context.connection); | primary_key = primary_key_field->generate_value(context.connection); | ||||
| statement.set(index, primary_key); | |||||
| if (statement) statement->set(index, primary_key); | |||||
| ++index; | ++index; | ||||
| } | } | ||||
| else | else | ||||
| @@ -1137,7 +1139,7 @@ std::string table_t::execute_create_update( | |||||
| if (!new_context.derived_table) | if (!new_context.derived_table) | ||||
| new_context.derived_table = this; | new_context.derived_table = this; | ||||
| std::string key = create_update_base(new_context); | std::string key = create_update_base(new_context); | ||||
| statement.set(index, std::move(key)); | |||||
| if (statement) statement->set(index, std::move(key)); | |||||
| ++index; | ++index; | ||||
| } | } | ||||
| @@ -1155,11 +1157,17 @@ std::string table_t::execute_create_update( | |||||
| /* insert/update dataset */ | /* insert/update dataset */ | ||||
| value_t key = field.foreign_create_update(context); | value_t key = field.foreign_create_update(context); | ||||
| if (key.has_value()) | if (key.has_value()) | ||||
| statement.set(index, std::move(key)); | |||||
| { | |||||
| if (statement) statement->set(index, std::move(key)); | |||||
| } | |||||
| else if (field.value_is_nullable) | else if (field.value_is_nullable) | ||||
| statement.set_null(index); | |||||
| { | |||||
| if (statement) statement->set_null(index); | |||||
| } | |||||
| else | else | ||||
| { | |||||
| throw misc::hibernate_exception("Received null key for non nullable foreign dataset!"); | throw misc::hibernate_exception("Received null key for non nullable foreign dataset!"); | ||||
| } | |||||
| ++index; | ++index; | ||||
| /* cleanup old dataset (if new one was created) */ | /* cleanup old dataset (if new one was created) */ | ||||
| @@ -1189,18 +1197,24 @@ std::string table_t::execute_create_update( | |||||
| if (set_value) | if (set_value) | ||||
| { | { | ||||
| assert(!context.owner_key.empty()); | assert(!context.owner_key.empty()); | ||||
| statement.set(index, context.owner_key); | |||||
| if (statement) statement->set(index, context.owner_key); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| statement.set_null(index); | |||||
| if (statement) statement->set_null(index); | |||||
| } | } | ||||
| ++index; | ++index; | ||||
| if (field_info.value_is_ordered) | 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; | ++index; | ||||
| } | } | ||||
| } | } | ||||
| @@ -1215,8 +1229,14 @@ std::string table_t::execute_create_update( | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| auto value = field_info.get(context); | 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; | ++index; | ||||
| } | } | ||||
| @@ -1225,7 +1245,7 @@ std::string table_t::execute_create_update( | |||||
| && !base_table | && !base_table | ||||
| && !is_update) | && !is_update) | ||||
| { | { | ||||
| statement.set(index, context.derived_table | |||||
| if (statement) statement->set(index, context.derived_table | |||||
| ? context.derived_table->table_id | ? context.derived_table->table_id | ||||
| : table_id); | : table_id); | ||||
| ++index; | ++index; | ||||
| @@ -1235,34 +1255,37 @@ std::string table_t::execute_create_update( | |||||
| if (is_update) | if (is_update) | ||||
| { | { | ||||
| assert(primary_key_field); | assert(primary_key_field); | ||||
| statement.set(index, *primary_key_field->get(context)); | |||||
| if (statement) statement->set(index, *primary_key_field->get(context)); | |||||
| ++index; | ++index; | ||||
| } | } | ||||
| /* execute */ | /* 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 */ | /* foreign table many fields */ | ||||
| for (auto& ptr : 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); | auto query = build_init_stage2_query(*this); | ||||
| _statement_alter_table.reset(new ::cppmariadb::statement(query)); | _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 | ::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; | 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 key = std::make_tuple(filter.cache_id, owner); | ||||
| auto it = _statement_update.find(key); | 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); | auto query = build_create_update_query(*this, &filter, owner); | ||||
| it = _statement_update.emplace(key, ::cppmariadb::statement(query)).first; | 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 | ::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 | 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_update(context.filter, context.owner_field) | ||||
| : get_statement_insert_into(); | : get_statement_insert_into(); | ||||
| return execute_create_update(context, statement); | return execute_create_update(context, statement); | ||||
| @@ -449,3 +449,72 @@ TEST(CppHibernateTests, create_derived3) | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| context.create(static_cast<derived2&>(d3)); | context.create(static_cast<derived2&>(d3)); | ||||
| } | } | ||||
| TEST(CppHibernateTests, create_dummy_owner) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0000-000000000001" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_owner` " | |||||
| "SET " | |||||
| "`tbl_dummy_owner_id`=UuidToBin('X00000000-0000-0000-0000-000000000001X')", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000001" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000001X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X0X', " | |||||
| "`data`='X123X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000002" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000002X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X1X', " | |||||
| "`data`='X456X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000003" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000003X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X2X', " | |||||
| "`data`='X789X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "COMMIT"); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||||
| .Times(AnyNumber()) | |||||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_close( | |||||
| reinterpret_cast<MYSQL*>(0x1111))); | |||||
| dummy_owner d; | |||||
| d.dummies.emplace_back(123); | |||||
| d.dummies.emplace_back(456); | |||||
| d.dummies.emplace_back(789); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.create(d); | |||||
| } | |||||
| @@ -151,6 +151,28 @@ TEST(CppHibernateTests, init) | |||||
| "ENGINE = InnoDB\n" | "ENGINE = InnoDB\n" | ||||
| "DEFAULT CHARACTER SET = utf8"); | "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" | 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" | ||||
| @@ -204,6 +226,13 @@ TEST(CppHibernateTests, init) | |||||
| " ON DELETE CASCADE\n" | " ON DELETE CASCADE\n" | ||||
| " ON UPDATE CASCADE"); | " 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_query(mock, "COMMIT"); | ||||
| EXPECT_CALL( | EXPECT_CALL( | ||||
| @@ -863,4 +863,58 @@ TEST(CppHibernateTests, read_base_ptr_vector_dynamic) | |||||
| } | } | ||||
| EXPECT_EQ(bIt, base_vec.end()); | EXPECT_EQ(bIt, base_vec.end()); | ||||
| } | |||||
| TEST(CppHibernateTests, read_dummy_owner) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_dummy_owner`.`tbl_dummy_owner_id`) " | |||||
| "FROM " | |||||
| "`tbl_dummy_owner` " | |||||
| "WHERE " | |||||
| "(`tbl_dummy_owner`.`tbl_dummy_owner_id`=UuidToBin('X00000000-0000-0000-0000-000000000001X')) ", | |||||
| result_used({ | |||||
| { "00000000-0000-0000-0000-000000000001" }, | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_dummy_id`.`tbl_dummy_id_id`), " | |||||
| "`tbl_dummy_id`.`data` " | |||||
| "FROM " | |||||
| "`tbl_dummy_id` WHERE (`tbl_dummy_id`.`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X')) " | |||||
| "ORDER BY " | |||||
| "`tbl_dummy_id`.`tbl_dummy_owner_index_dummies` ASC ", | |||||
| result_used({ | |||||
| { "00000000-0000-0000-0001-000000000001", "123" }, | |||||
| { "00000000-0000-0000-0001-000000000002", "456" }, | |||||
| { "00000000-0000-0000-0001-000000000003", "789" }, | |||||
| })); | |||||
| expect_query(mock, "COMMIT"); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_close( | |||||
| reinterpret_cast<MYSQL*>(0x1111))); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||||
| .Times(AnyNumber()) | |||||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||||
| dummy_owner d; | |||||
| d.id = uuid("00000000-0000-0000-0000-000000000001"); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.read(d); | |||||
| EXPECT_EQ(d.id, uuid("00000000-0000-0000-0000-000000000001")); | |||||
| ASSERT_EQ(d.dummies.size(), 3); | |||||
| EXPECT_EQ(d.dummies[0].data, 123); | |||||
| EXPECT_EQ(d.dummies[1].data, 456); | |||||
| EXPECT_EQ(d.dummies[2].data, 789); | |||||
| } | } | ||||
| @@ -634,4 +634,79 @@ TEST(CppHibernateTests, update_dynamic_base) | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::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(b); | context.update(b); | ||||
| } | |||||
| TEST(CppHibernateTests, update_dummy_owner) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "UPDATE " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_owner_id_dummies`=NULL, " | |||||
| "`tbl_dummy_owner_index_dummies`=0 " | |||||
| "WHERE " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X')"); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000001" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000001X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X0X', " | |||||
| "`data`='X123X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000002" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000002X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X1X', " | |||||
| "`data`='X456X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||||
| { "00000000-0000-0000-0001-000000000003" } | |||||
| })); | |||||
| expect_query(mock, "INSERT INTO " | |||||
| "`tbl_dummy_id` " | |||||
| "SET " | |||||
| "`tbl_dummy_id_id`=UuidToBin('X00000000-0000-0000-0001-000000000003X'), " | |||||
| "`tbl_dummy_owner_id_dummies`=UuidToBin('X00000000-0000-0000-0000-000000000001X'), " | |||||
| "`tbl_dummy_owner_index_dummies`='X2X', " | |||||
| "`data`='X789X'", | |||||
| result_affected_rows(1)); | |||||
| expect_query(mock, "DELETE " | |||||
| "`tbl_dummy_id` " | |||||
| "FROM " | |||||
| "`tbl_dummy_id` " | |||||
| "WHERE " | |||||
| "(`tbl_dummy_owner_id_dummies` IS NULL)"); | |||||
| expect_query(mock, "COMMIT"); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||||
| .Times(AnyNumber()) | |||||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||||
| EXPECT_CALL( | |||||
| mock, | |||||
| mysql_close( | |||||
| reinterpret_cast<MYSQL*>(0x1111))); | |||||
| dummy_owner d; | |||||
| d.id = uuid("00000000-0000-0000-0000-000000000001"); | |||||
| d.dummies.emplace_back(123); | |||||
| d.dummies.emplace_back(456); | |||||
| d.dummies.emplace_back(789); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| context.update(d); | |||||
| } | } | ||||
| @@ -93,6 +93,21 @@ struct derived3 | |||||
| std::vector<test3> test3_vector; | 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( | constexpr decltype(auto) test_schema = cpphibernate_make_schema( | ||||
| test, | test, | ||||
| cpphibernate_make_table_name( | cpphibernate_make_table_name( | ||||
| @@ -158,5 +173,19 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||||
| cpphibernate_make_id (&derived3::derived3_id), | cpphibernate_make_id (&derived3::derived3_id), | ||||
| cpphibernate_make_field (derived3, test3_list), | cpphibernate_make_field (derived3, test3_list), | ||||
| cpphibernate_make_field (derived3, test3_vector) | 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) | |||||
| ) | ) | ||||
| ); | ); | ||||