* implemented read methods of foreign many tables for mariadb drivermaster
| @@ -50,9 +50,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| template<typename T_context, typename T_data> | template<typename T_context, typename T_data> | ||||
| constexpr decltype(auto) operator()(const T_context& context, T_data& data) const | constexpr decltype(auto) operator()(const T_context& context, T_data& data) const | ||||
| { | { | ||||
| auto new_context = context; | |||||
| new_context.data_id = misc::get_type_id(hana::type_c<mp::decay_t<T_data>>); | |||||
| new_context.data = &data; | |||||
| auto new_context = context; | |||||
| new_context.set(data); | |||||
| return new_context; | return new_context; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -68,12 +67,26 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| private: | private: | ||||
| friend __impl::change_context_impl; | friend __impl::change_context_impl; | ||||
| size_t data_id; | |||||
| void* data; | |||||
| mutable size_t _dataset_id; | |||||
| mutable void* _dataset; | |||||
| mutable const table_t* _table; | |||||
| protected: | |||||
| template<typename T_dataset> | |||||
| inline void* set(T_dataset& dataset, size_t dataset_id = 0) const; | |||||
| public: | public: | ||||
| template<typename T> | |||||
| inline decltype(auto) get(const table_t* table = nullptr) const; | |||||
| template<typename T_dataset> | |||||
| inline decltype(auto) get() const; | |||||
| inline data_context( | |||||
| const schema_t& p_schema, | |||||
| ::cppmariadb::connection& p_connection) | |||||
| : base_context (p_schema, p_connection) | |||||
| , _dataset_id (0) | |||||
| , _dataset (nullptr) | |||||
| , _table (nullptr) | |||||
| { } | |||||
| template<typename T_data> | template<typename T_data> | ||||
| inline data_context( | inline data_context( | ||||
| @@ -81,8 +94,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| const schema_t& p_schema, | const schema_t& p_schema, | ||||
| ::cppmariadb::connection& p_connection) | ::cppmariadb::connection& p_connection) | ||||
| : base_context (p_schema, p_connection) | : base_context (p_schema, p_connection) | ||||
| , data_id (misc::get_type_id(hana::type_c<mp::decay_t<T_data>>)) | |||||
| , data (&p_data) | |||||
| , _dataset_id (misc::get_type_id(hana::type_c<mp::decay_t<T_data>>)) | |||||
| , _dataset (&p_data) | |||||
| , _table (nullptr) | |||||
| { } | { } | ||||
| }; | }; | ||||
| @@ -93,6 +107,14 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| { | { | ||||
| const filter_t& filter; | const filter_t& filter; | ||||
| inline filter_context( | |||||
| const schema_t& p_schema, | |||||
| ::cppmariadb::connection& p_connection, | |||||
| const filter_t& p_filter) | |||||
| : data_context (p_schema, p_connection) | |||||
| , filter (p_filter) | |||||
| { } | |||||
| template<typename T_data> | template<typename T_data> | ||||
| inline filter_context( | inline filter_context( | ||||
| T_data& p_data, | T_data& p_data, | ||||
| @@ -135,40 +157,40 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| struct read_context | struct read_context | ||||
| : public filter_context | : public filter_context | ||||
| { | { | ||||
| bool is_dynamic; | |||||
| protected: | |||||
| size_t base_dataset_id; | size_t base_dataset_id; | ||||
| public: | |||||
| bool is_dynamic; | |||||
| std::string where; | std::string where; | ||||
| std::string limit; | std::string limit; | ||||
| std::string order_by; | std::string order_by; | ||||
| template<typename T_data> | |||||
| protected: | |||||
| inline read_context( | inline read_context( | ||||
| T_data& p_data, | |||||
| const schema_t& p_schema, | const schema_t& p_schema, | ||||
| ::cppmariadb::connection& p_connection, | ::cppmariadb::connection& p_connection, | ||||
| const filter_t& p_filter) | const filter_t& p_filter) | ||||
| : filter_context (p_data, p_schema, p_connection, p_filter) | |||||
| : filter_context (p_schema, p_connection, p_filter) | |||||
| , is_dynamic (false) | , is_dynamic (false) | ||||
| , base_dataset_id (0) | , base_dataset_id (0) | ||||
| { } | { } | ||||
| public: | |||||
| virtual ~read_context() = default; | virtual ~read_context() = default; | ||||
| template<typename T_dataset> | template<typename T_dataset> | ||||
| inline T_dataset& emplace(const table_t* table = nullptr) const; | inline T_dataset& emplace(const table_t* table = nullptr) const; | ||||
| void emplace() const | void emplace() const | ||||
| { emplace_intern(nullptr); } | |||||
| { emplace_intern(nullptr, 0); } | |||||
| void finish() const | void finish() const | ||||
| { finish_intern(); } | { finish_intern(); } | ||||
| private: | private: | ||||
| virtual void* emplace_intern(void* data) const | |||||
| { throw misc::hibernate_exception("read_context::emplace_intern is not implemented!"); } | |||||
| virtual void finish_intern() const | |||||
| { throw misc::hibernate_exception("read_context::finish_intern is not implemented!"); } | |||||
| virtual void* emplace_intern(void* data, size_t dataset_id) const = 0; | |||||
| virtual void finish_intern () const = 0; | |||||
| }; | }; | ||||
| using read_context_ptr = std::unique_ptr<read_context>; | using read_context_ptr = std::unique_ptr<read_context>; | ||||
| @@ -7,29 +7,50 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| /* data_context */ | /* data_context */ | ||||
| template<typename T> | |||||
| template<typename T_dataset> | |||||
| inline void* data_context | |||||
| ::set(T_dataset& dataset, size_t dataset_id) const | |||||
| { | |||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| _table = nullptr; | |||||
| _dataset = &dataset; | |||||
| _dataset_id = (dataset_id == 0) | |||||
| ? misc::get_type_id(hana::type_c<dataset_type>) | |||||
| : dataset_id; | |||||
| return _dataset; | |||||
| } | |||||
| template<typename T_dataset> | |||||
| inline decltype(auto) data_context | inline decltype(auto) data_context | ||||
| ::get(const table_t* table) const | |||||
| ::get() const | |||||
| { | { | ||||
| if (!data) | |||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| if (!_dataset) | |||||
| throw misc::hibernate_exception("no data assigned!"); | throw misc::hibernate_exception("no data assigned!"); | ||||
| auto type_id = misc::get_type_id(hana::type_c<mp::decay_t<T>>); | |||||
| if (type_id != data_id) | |||||
| auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||||
| if (dataset_id != _dataset_id) | |||||
| { | { | ||||
| if (!table) | |||||
| table = &schema.table(type_id); | |||||
| /* check table */ | |||||
| if (!_table) | |||||
| _table = &schema.table(_dataset_id); | |||||
| else if (_table->dataset_id != _dataset_id) | |||||
| throw misc::hibernate_exception("invalid table!"); | |||||
| while(table && table->dataset_id != type_id) | |||||
| auto table = _table; | |||||
| while(table && table->dataset_id != dataset_id) | |||||
| table = table->base_table; | table = table->base_table; | ||||
| if (!table) | if (!table) | ||||
| { | { | ||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||||
| << "invalid type! expected " << data_id << ", got " << type_id).str()); | |||||
| throw misc::hibernate_exception(utl::type_helper<dataset_type>::name() + | |||||
| " is not a derived type of dataset with id " + std::to_string(_dataset_id)); | |||||
| } | } | ||||
| } | } | ||||
| return *static_cast<T*>(data); | |||||
| return *static_cast<dataset_type*>(_dataset); | |||||
| } | } | ||||
| /* read_context */ | /* read_context */ | ||||
| @@ -38,29 +59,32 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| T_dataset& read_context | T_dataset& read_context | ||||
| ::emplace(const table_t* table) const | ::emplace(const table_t* table) const | ||||
| { | { | ||||
| if (!is_dynamic || base_dataset_id == 0) | |||||
| throw misc::hibernate_exception("dynamic creation is deactivated for this context!"); | |||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| // check table | // check table | ||||
| auto dataset_id = misc::get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||||
| auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||||
| if (!table) | if (!table) | ||||
| table = &schema.table(dataset_id); | table = &schema.table(dataset_id); | ||||
| else if (table->dataset_id != dataset_id) | else if (table->dataset_id != dataset_id) | ||||
| throw misc::hibernate_exception("dataset id of table and dataset to insert defer!"); | |||||
| throw misc::hibernate_exception("wrong table passed!"); | |||||
| // check base | // check base | ||||
| auto tbl = table; | auto tbl = table; | ||||
| while (tbl && tbl->dataset_id != base_dataset_id) | while (tbl && tbl->dataset_id != base_dataset_id) | ||||
| tbl = tbl->base_table; | tbl = tbl->base_table; | ||||
| if (!tbl) | if (!tbl) | ||||
| throw misc::hibernate_exception(utl::type_helper<T_dataset>::name() + " is not a derived type of table " + table->table_name); | |||||
| { | |||||
| throw misc::hibernate_exception(utl::type_helper<dataset_type>::name() + | |||||
| " is not a derived type of dataset with id " + std::to_string(base_dataset_id)); | |||||
| } | |||||
| // create dataset | // create dataset | ||||
| auto ptr = std::make_unique<T_dataset>(); | |||||
| auto ret = emplace_intern(ptr.get()); | |||||
| if (!ret) | |||||
| ret = ptr.release(); | |||||
| return *static_cast<T_dataset*>(ret); | |||||
| auto ptr = std::make_unique<dataset_type>(); | |||||
| auto data = emplace_intern(ptr.get(), dataset_id); | |||||
| if (!data) | |||||
| throw misc::hibernate_exception("unable to store created dataset in context!"); | |||||
| ptr.release(); | |||||
| return *static_cast<dataset_type*>(data); | |||||
| } | } | ||||
| } | } | ||||
| @@ -9,85 +9,228 @@ | |||||
| beg_namespace_cpphibernate_driver_mariadb | beg_namespace_cpphibernate_driver_mariadb | ||||
| { | { | ||||
| /* read_impl_t */ | |||||
| template<typename T_dataset, typename = void> | |||||
| struct read_impl_t | |||||
| namespace __impl | |||||
| { | { | ||||
| using dataset_type = T_dataset; | |||||
| struct context_impl | |||||
| : public read_context | |||||
| template<typename T, typename = void> | |||||
| struct make_read_context_impl | |||||
| { | { | ||||
| mutable size_t count; | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); } | |||||
| }; | |||||
| template<typename T_read_context> | |||||
| context_impl(T_read_context&& p_read_context) | |||||
| : read_context (std::forward<T_read_context>(p_read_context)) | |||||
| , count (0) | |||||
| { } | |||||
| template<typename T_dataset, typename... T_args> | |||||
| struct make_read_context_impl< | |||||
| mp::list<T_dataset, T_args...>, | |||||
| mp::enable_if_c< | |||||
| !misc::is_container<mp::decay_t<T_dataset>>::value | |||||
| && !misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||||
| { | |||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| private: | |||||
| virtual void* emplace_intern(void* data) const override | |||||
| struct context_impl | |||||
| : public read_context | |||||
| { | { | ||||
| if (data) | |||||
| throw misc::hibernate_exception("This context has a pre assigned dataset and can therefor not work in dynamic mode!"); | |||||
| ++count; | |||||
| if (count > 1) | |||||
| throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||||
| return nullptr; | |||||
| } | |||||
| virtual void finish_intern() const override | |||||
| { | |||||
| if (count < 1) | |||||
| throw misc::hibernate_exception("Expected exactly one dataset, but received none!"); | |||||
| } | |||||
| private: | |||||
| mutable size_t _count; | |||||
| dataset_type& _dataset; | |||||
| public: | |||||
| template<typename... X_args> | |||||
| context_impl(dataset_type& dataset, X_args&&... args) | |||||
| : read_context (std::forward<X_args>(args)...) | |||||
| , _count (0) | |||||
| , _dataset (dataset) | |||||
| { | |||||
| is_dynamic = false; | |||||
| base_dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||||
| } | |||||
| private: | |||||
| virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||||
| { | |||||
| if (data || dataset_id != 0) | |||||
| throw misc::hibernate_exception("Static datasets can not be assigned!"); | |||||
| ++_count; | |||||
| if (_count > 1) | |||||
| throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||||
| return set(_dataset); | |||||
| } | |||||
| virtual void finish_intern() const override | |||||
| { | |||||
| if (_count < 1) | |||||
| throw misc::hibernate_exception("Expected exactly one dataset, but received none!"); | |||||
| } | |||||
| }; | |||||
| static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||||
| { return context_impl(dataset, std::forward<T_args>(args)...); } | |||||
| }; | }; | ||||
| static inline void apply(const read_context& context) | |||||
| template<typename T_dataset, typename... T_args> | |||||
| struct make_read_context_impl< | |||||
| mp::list<T_dataset, T_args...>, | |||||
| mp::enable_if_c< | |||||
| !misc::is_container<mp::decay_t<T_dataset>>::value | |||||
| && misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||||
| { | { | ||||
| 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); | |||||
| transaction_lock trans(connection); | |||||
| table.read(context_impl(context)); | |||||
| trans.commit(); | |||||
| } | |||||
| }; | |||||
| /* read_impl_t - nullable */ | |||||
| template<typename T_dataset> | |||||
| struct read_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>; | |||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| using nullable_helper_type = misc::nullable_helper<dataset_type>; | |||||
| using value_type = typename nullable_helper_type::value_type; | |||||
| static inline void apply(const read_context& context) | |||||
| struct context_impl | |||||
| : public read_context | |||||
| { | |||||
| private: | |||||
| dataset_type& _dataset; | |||||
| mutable size_t _count; | |||||
| public: | |||||
| template<typename... X_args> | |||||
| context_impl(dataset_type& dataset, X_args&&... args) | |||||
| : read_context (std::forward<X_args>(args)...) | |||||
| , _dataset (dataset) | |||||
| , _count (0) | |||||
| { | |||||
| is_dynamic = misc::is_pointer<dataset_type>::value; | |||||
| base_dataset_id = misc::get_type_id(hana::type_c<value_type>); | |||||
| nullable_helper_type::clear(_dataset); | |||||
| } | |||||
| private: | |||||
| virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||||
| { | |||||
| if (data && !misc::is_pointer<dataset_type>::value) | |||||
| throw misc::hibernate_exception("This is not a pointer type!"); | |||||
| ++_count; | |||||
| if (_count > 1) | |||||
| throw misc::hibernate_exception("Expected exactly one dataset, but received more!"); | |||||
| if (data) | |||||
| { | |||||
| auto* cast = static_cast<value_type*>(data); | |||||
| auto& value = nullable_helper_type::set(_dataset, cast); | |||||
| if (cast != &value) | |||||
| throw misc::hibernate_exception("Nullable pointer value has changed!"); | |||||
| return set(value, dataset_id); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto& value = nullable_helper_type::set(_dataset, value_type { }); | |||||
| return set(value); | |||||
| } | |||||
| } | |||||
| virtual void finish_intern() const override | |||||
| { } | |||||
| }; | |||||
| static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||||
| { return context_impl(dataset, std::forward<T_args>(args)...); } | |||||
| }; | |||||
| template<typename T_dataset, typename... T_args> | |||||
| struct make_read_context_impl< | |||||
| mp::list<T_dataset, T_args...>, | |||||
| mp::enable_if_c< | |||||
| misc::is_container<mp::decay_t<T_dataset>>::value | |||||
| && !misc::is_nullable<mp::decay_t<T_dataset>>::value>> | |||||
| { | { | ||||
| using dataset_type = mp::decay_t<T_dataset>; | |||||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||||
| using container_helper_type = misc::container_helper<dataset_type>; | |||||
| using value_type = typename container_helper_type::value_type; | |||||
| struct context_impl | |||||
| : public read_context | |||||
| { | |||||
| private: | |||||
| dataset_type& _dataset; | |||||
| mutable size_t _count; | |||||
| public: | |||||
| template<typename... X_args> | |||||
| context_impl(dataset_type& dataset, X_args&&... args) | |||||
| : read_context (std::forward<X_args>(args)...) | |||||
| , _dataset (dataset) | |||||
| , _count (0) | |||||
| { | |||||
| is_dynamic = misc::is_pointer<value_type>::value; | |||||
| base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||||
| container_helper_type::clear(_dataset); | |||||
| } | |||||
| private: | |||||
| virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||||
| { | |||||
| if (data || dataset_id != 0) | |||||
| throw misc::hibernate_exception("Static datasets can not be assigned!"); | |||||
| auto& value = container_helper_type::emplace(_dataset); | |||||
| return set(value); | |||||
| } | |||||
| virtual void finish_intern() const override | |||||
| { } | |||||
| }; | |||||
| static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args) | |||||
| { return context_impl(dataset, std::forward<T_args>(args)...); } | |||||
| }; | |||||
| } | |||||
| }; | |||||
| } | |||||
| /* read_impl_t - container */ | |||||
| constexpr decltype(auto) make_read_context = misc::make_generic_predicate<__impl::make_read_context_impl> { }; | |||||
| template<typename T_dataset> | |||||
| struct read_impl_t< | |||||
| T_dataset, | |||||
| mp::enable_if<misc::is_container<T_dataset>>> | |||||
| namespace __impl | |||||
| { | { | ||||
| using dataset_type = T_dataset; | |||||
| static inline void apply(const read_context& context) | |||||
| template<typename T, typename = void> | |||||
| struct make_fake_context_impl | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fake_context(...)!"); } | |||||
| }; | |||||
| template<typename T_wrapped_dataset, typename... T_args> | |||||
| struct make_fake_context_impl< | |||||
| mp::list<T_wrapped_dataset, T_args...>, | |||||
| mp::enable_if_c< | |||||
| hana::is_a_t<hana::type_tag, T_wrapped_dataset>::value>> | |||||
| { | { | ||||
| using wrapped_dataset_type = mp::decay_t<T_wrapped_dataset>; | |||||
| using dataset_type = misc::unwrap_t<wrapped_dataset_type>; | |||||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||||
| struct context_impl | |||||
| : public read_context | |||||
| { | |||||
| public: | |||||
| template<typename... X_args> | |||||
| context_impl(X_args&&... args) | |||||
| : read_context (std::forward<X_args>(args)...) | |||||
| { | |||||
| is_dynamic = misc::is_pointer<dataset_type>::value; | |||||
| base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||||
| } | |||||
| private: | |||||
| virtual void* emplace_intern(void* data, size_t dataset_id) const override | |||||
| { return nullptr; } | |||||
| virtual void finish_intern() const override | |||||
| { } | |||||
| }; | |||||
| static constexpr decltype(auto) apply(T_wrapped_dataset&&, T_args&&... args) | |||||
| { return context_impl(std::forward<T_args>(args)...); } | |||||
| }; | |||||
| } | |||||
| } | |||||
| }; | |||||
| constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { }; | |||||
| } | } | ||||
| end_namespace_cpphibernate_driver_mariadb | end_namespace_cpphibernate_driver_mariadb | ||||
| @@ -98,7 +98,6 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| }, | }, | ||||
| []{ }); | []{ }); | ||||
| }); | }); | ||||
| os << ")"; | |||||
| statement.assign(os.str()); | statement.assign(os.str()); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -44,11 +44,19 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| template<typename T_dataset, typename T_modifiers> | template<typename T_dataset, typename T_modifiers> | ||||
| inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const | inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const | ||||
| { | { | ||||
| read_context context(dataset, _schema, _connection, _filter); | |||||
| 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); | |||||
| auto context = make_read_context(dataset, _schema, _connection, _filter); | |||||
| context.where = build_where(_schema, modifiers).query(_connection); | context.where = build_where(_schema, modifiers).query(_connection); | ||||
| context.limit = build_limit(modifiers).query(_connection); | context.limit = build_limit(modifiers).query(_connection); | ||||
| context.order_by = build_order_by(_schema, modifiers).query(_connection); | context.order_by = build_order_by(_schema, modifiers).query(_connection); | ||||
| read_impl_t<T_dataset>::apply(context); | |||||
| transaction_lock trans(_connection); | |||||
| table.read(context); | |||||
| trans.commit(); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -6,7 +6,7 @@ | |||||
| beg_namespace_cpphibernate_driver_mariadb | beg_namespace_cpphibernate_driver_mariadb | ||||
| { | { | ||||
| /* field_t */ | /* field_t */ | ||||
| struct field_t; | struct field_t; | ||||
| @@ -24,6 +24,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| size_t real_value_id { 0 }; // unique id of the real/unwrapped value type | size_t real_value_id { 0 }; // unique id of the real/unwrapped value type | ||||
| bool value_is_nullable { false }; // value is stored in a nullable container | bool value_is_nullable { false }; // value is stored in a nullable container | ||||
| bool value_is_pointer { false }; // value is stored in a pointer container | |||||
| bool value_is_container { false }; // value is stored in a container | bool value_is_container { false }; // value is stored in a container | ||||
| 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 | ||||
| @@ -53,6 +54,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| , value_id (std::move(other).value_id) | , value_id (std::move(other).value_id) | ||||
| , real_value_id (std::move(other).real_value_id) | , real_value_id (std::move(other).real_value_id) | ||||
| , value_is_nullable (std::move(other).value_is_nullable) | , value_is_nullable (std::move(other).value_is_nullable) | ||||
| , value_is_pointer (std::move(other).value_is_pointer) | |||||
| , value_is_container (std::move(other).value_is_container) | , value_is_container (std::move(other).value_is_container) | ||||
| , value_is_auto_incremented (std::move(other).value_is_auto_incremented) | , value_is_auto_incremented (std::move(other).value_is_auto_incremented) | ||||
| , table (nullptr) | , table (nullptr) | ||||
| @@ -77,7 +79,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| using read_context_ptr = std::unique_ptr<read_context>; | using read_context_ptr = std::unique_ptr<read_context>; | ||||
| virtual value_t foreign_create_update (const create_update_context& context) const; | virtual value_t foreign_create_update (const create_update_context& context) const; | ||||
| virtual read_context_ptr foreign_read (const read_context& context, const value_t& value) const; | |||||
| virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const; | |||||
| /* properties */ | /* properties */ | ||||
| virtual value_t get (const data_context& context) const; | virtual value_t get (const data_context& context) const; | ||||
| @@ -180,7 +182,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| public: | public: | ||||
| virtual value_t foreign_create_update(const create_update_context& context) const override; | virtual value_t foreign_create_update(const create_update_context& context) const override; | ||||
| virtual read_context_ptr foreign_read (const read_context& context, const value_t& value) const override; | |||||
| virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const override; | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -21,6 +21,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| real_value_id = misc::get_type_id(hana::type_c<real_value_type>); | real_value_id = misc::get_type_id(hana::type_c<real_value_type>); | ||||
| value_is_nullable = misc::is_nullable<value_type>::value; | value_is_nullable = misc::is_nullable<value_type>::value; | ||||
| value_is_pointer = misc::is_pointer<value_type>::value; | |||||
| value_is_container = misc::is_container<value_type>::value; | value_is_container = misc::is_container<value_type>::value; | ||||
| value_is_ordered = misc::is_ordered<value_type>::value; | value_is_ordered = misc::is_ordered<value_type>::value; | ||||
| } | } | ||||
| @@ -39,7 +40,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| value_t value_field_t<T_field> | value_t value_field_t<T_field> | ||||
| ::get(const data_context& context) const | ::get(const data_context& context) const | ||||
| { | { | ||||
| auto& dataset = context.get<dataset_type>(this->table); | |||||
| auto& dataset = context.get<dataset_type>(); | |||||
| return type_props::convert_from(this->field.getter(dataset)); | return type_props::convert_from(this->field.getter(dataset)); | ||||
| } | } | ||||
| @@ -47,7 +48,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| void value_field_t<T_field> | void value_field_t<T_field> | ||||
| ::set(const data_context& context, const value_t& value) const | ::set(const data_context& context, const value_t& value) const | ||||
| { | { | ||||
| auto& dataset = context.get<dataset_type>(this->table); | |||||
| auto& dataset = context.get<dataset_type>(); | |||||
| this->field.setter(dataset, type_props::convert_to(value)); | this->field.setter(dataset, type_props::convert_to(value)); | ||||
| } | } | ||||
| @@ -89,38 +90,22 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| template<typename T_field> | template<typename T_field> | ||||
| read_context_ptr foreign_table_field_t<T_field> | read_context_ptr foreign_table_field_t<T_field> | ||||
| ::foreign_read(const read_context& context, const value_t& value) const | |||||
| ::foreign_read(const read_context& context, bool fake_context) const | |||||
| { | { | ||||
| using is_nullable_type = misc::is_nullable<value_type>; | |||||
| return hana::eval_if( | |||||
| is_nullable_type { }, | |||||
| [&](auto _) { | |||||
| auto& dataset = _(context).template get<dataset_type>(); | |||||
| auto& member = this->field.getter(dataset); | |||||
| using nullable_helper_type = misc::nullable_helper<mp::decay_t<decltype(member)>>; | |||||
| if (value.has_value()) | |||||
| { | |||||
| auto& new_dataset = nullable_helper_type::set(member, real_value_type { }); | |||||
| auto new_context = change_context(context, new_dataset); | |||||
| return std::make_unique<read_context>(new_context); | |||||
| } | |||||
| else | |||||
| { | |||||
| nullable_helper_type::clear(member); | |||||
| return read_context_ptr { }; | |||||
| } | |||||
| }, | |||||
| [&](auto _) { | |||||
| if (!value.has_value()) | |||||
| { | |||||
| throw misc::hibernate_exception(std::string("excepted value for field ") + | |||||
| this->table_name + "." + this->field_name + " but received null!"); | |||||
| } | |||||
| auto& dataset = context.get<dataset_type>(); | |||||
| auto& member = this->field.getter(dataset); | |||||
| auto new_context = change_context(context, member); | |||||
| return std::make_unique<read_context>(new_context); | |||||
| }); | |||||
| if (!fake_context) | |||||
| { | |||||
| auto& dataset = context.get<dataset_type>(); | |||||
| auto& member = this->field.getter(dataset); | |||||
| auto new_context = make_read_context(member, context.schema, context.connection, context.filter); | |||||
| using context_type = mp::decay_t<decltype(new_context)>; | |||||
| return std::make_unique<context_type>(new_context); | |||||
| } | |||||
| else | |||||
| { | |||||
| auto new_context = make_fake_context(hana::type_c<value_type>, context.schema, context.connection, context.filter); | |||||
| using context_type = mp::decay_t<decltype(new_context)>; | |||||
| return std::make_unique<context_type>(new_context); | |||||
| } | |||||
| } | } | ||||
| namespace __impl | namespace __impl | ||||
| @@ -6,7 +6,7 @@ | |||||
| beg_namespace_cpphibernate_driver_mariadb | beg_namespace_cpphibernate_driver_mariadb | ||||
| { | { | ||||
| /* table_t */ | /* table_t */ | ||||
| struct table_t; | struct table_t; | ||||
| @@ -64,7 +64,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| void print(std::ostream& os) const; | void print(std::ostream& os) const; | ||||
| const table_t* get_derived(size_t id) const; | |||||
| 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; | |||||
| /* CRUD */ | /* CRUD */ | ||||
| inline void init_stage1(const init_context& context) const | inline void init_stage1(const init_context& context) const | ||||
| @@ -158,9 +161,12 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| using table_type = typename base_type::table_type; | using table_type = typename base_type::table_type; | ||||
| using base_dataset_type = typename base_type::base_dataset_type; | using base_dataset_type = typename base_type::base_dataset_type; | ||||
| using dataset_type = typename table_type::dataset_type; | using dataset_type = typename table_type::dataset_type; | ||||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||||
| using base_type::base_type; | using base_type::base_type; | ||||
| virtual void emplace(const read_context& context) const override; | |||||
| private: | private: | ||||
| template<typename T_dataset, typename T_pred, typename T_include_self> | template<typename T_dataset, typename T_pred, typename T_include_self> | ||||
| constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; | constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; | ||||
| @@ -8,6 +8,11 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| /* table_polymorphic_t */ | /* table_polymorphic_t */ | ||||
| template<typename T_schema, typename T_table, typename T_base_dataset> | |||||
| void table_polymorphic_t<T_schema, T_table, T_base_dataset> | |||||
| ::emplace(const read_context& context) const | |||||
| { context.emplace<real_dataset_type>(this); } | |||||
| template<typename T_schema, typename T_table, typename T_base_dataset> | template<typename T_schema, typename T_table, typename T_base_dataset> | ||||
| template<typename T_dataset, typename T_pred, typename T_include_self> | template<typename T_dataset, typename T_pred, typename T_include_self> | ||||
| constexpr void table_polymorphic_t<T_schema, T_table, T_base_dataset> | constexpr void table_polymorphic_t<T_schema, T_table, T_base_dataset> | ||||
| @@ -42,7 +47,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| { | { | ||||
| using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>; | 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_dataset_id = misc::get_type_id(hana::type_c<derived_dataset_type>); | ||||
| auto derived_table = this->get_derived(derived_dataset_id); | |||||
| auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); | |||||
| if (!derived_table) | if (!derived_table) | ||||
| { | { | ||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | ||||
| @@ -71,7 +76,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| }, | }, | ||||
| [this, &context](auto _)->std::string { | [this, &context](auto _)->std::string { | ||||
| using tmp_type = misc::decay_unwrap_t<decltype(_(hana::type_c<base_dataset_type>))>; | using tmp_type = misc::decay_unwrap_t<decltype(_(hana::type_c<base_dataset_type>))>; | ||||
| assert(base_table); | |||||
| assert(this->base_table); | |||||
| auto& dataset = context.get<dataset_type>(); | auto& dataset = context.get<dataset_type>(); | ||||
| auto& base = static_cast<tmp_type&>(dataset); | auto& base = static_cast<tmp_type&>(dataset); | ||||
| return this->base_table->create_update_exec(change_context(context, base)); | return this->base_table->create_update_exec(change_context(context, base)); | ||||
| @@ -1,5 +1,6 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <cpphibernate/misc/container_helper.h> | |||||
| #include <cpphibernate/misc/general.h> | #include <cpphibernate/misc/general.h> | ||||
| #include <cpphibernate/misc/meta.h> | #include <cpphibernate/misc/meta.h> | ||||
| #include <cpphibernate/misc/nullable_helper.h> | #include <cpphibernate/misc/nullable_helper.h> | ||||
| @@ -1,5 +1,7 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <iostream> // TODO debug! | |||||
| #include <cpphibernate/config.h> | #include <cpphibernate/config.h> | ||||
| #include <cpputils/misc/type_helper.h> | #include <cpputils/misc/type_helper.h> | ||||
| @@ -37,6 +37,9 @@ beg_namespace_cpphibernate_misc | |||||
| static inline const value_type* get(const nullable_type& x) | static inline const value_type* get(const nullable_type& x) | ||||
| { return x.has_value() ? &x.value() : nullptr; } | { return x.has_value() ? &x.value() : nullptr; } | ||||
| static inline value_type& set(nullable_type& x, const value_type* value) | |||||
| { return *(x = *value); } | |||||
| static inline value_type& set(nullable_type& x, const value_type& value) | static inline value_type& set(nullable_type& x, const value_type& value) | ||||
| { return *(x = value); } | { return *(x = value); } | ||||
| @@ -58,9 +61,9 @@ beg_namespace_cpphibernate_misc | |||||
| static inline value_type* get(const nullable_type& x) | static inline value_type* get(const nullable_type& x) | ||||
| { return x.get(); } | { return x.get(); } | ||||
| static inline value_type& set(nullable_type& x, value_type&& value) | |||||
| static inline value_type& set(nullable_type& x, value_type* value) | |||||
| { | { | ||||
| x.reset(new value_type(std::move(value))); | |||||
| x.reset(value); | |||||
| return *x; | return *x; | ||||
| } | } | ||||
| @@ -70,6 +73,12 @@ beg_namespace_cpphibernate_misc | |||||
| return *x; | return *x; | ||||
| } | } | ||||
| static inline value_type& set(nullable_type& x, value_type&& value) | |||||
| { | |||||
| x.reset(new value_type(std::move(value))); | |||||
| return *x; | |||||
| } | |||||
| static void clear(nullable_type& x) | static void clear(nullable_type& x) | ||||
| { return x.reset(); } | { return x.reset(); } | ||||
| }; | }; | ||||
| @@ -85,9 +94,9 @@ beg_namespace_cpphibernate_misc | |||||
| static inline value_type* get(const nullable_type& x) | static inline value_type* get(const nullable_type& x) | ||||
| { return x.get(); } | { return x.get(); } | ||||
| static inline value_type& set(nullable_type& x, value_type&& value) | |||||
| static inline value_type& set(nullable_type& x, value_type* value) | |||||
| { | { | ||||
| x.reset(new value_type(std::move(value))); | |||||
| x.reset(value); | |||||
| return *x; | return *x; | ||||
| } | } | ||||
| @@ -97,6 +106,12 @@ beg_namespace_cpphibernate_misc | |||||
| return *x; | return *x; | ||||
| } | } | ||||
| static inline value_type& set(nullable_type& x, value_type&& value) | |||||
| { | |||||
| x.reset(new value_type(std::move(value))); | |||||
| return *x; | |||||
| } | |||||
| static void clear(nullable_type& x) | static void clear(nullable_type& x) | ||||
| { return x.reset(); } | { return x.reset(); } | ||||
| }; | }; | ||||
| @@ -24,6 +24,7 @@ void field_t::print(std::ostream& os) const | |||||
| << indent << "\"value_id\": " << value_id << "," | << indent << "\"value_id\": " << value_id << "," | ||||
| << indent << "\"real_value_id\": " << real_value_id << "," | << indent << "\"real_value_id\": " << real_value_id << "," | ||||
| << indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," | << indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," | ||||
| << indent << "\"value_is_pointer\": " << (value_is_pointer ? "true" : "false") << "," | |||||
| << indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," | << indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," | ||||
| << indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," | << indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," | ||||
| << indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," | << indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," | ||||
| @@ -150,7 +151,7 @@ void field_t::update() | |||||
| /* CRUD */ | /* CRUD */ | ||||
| throw_not_implemented(value_t, foreign_create_update, const create_update_context&) | throw_not_implemented(value_t, foreign_create_update, const create_update_context&) | ||||
| throw_not_implemented(read_context_ptr, foreign_read, const read_context&, const value_t&) | |||||
| throw_not_implemented(read_context_ptr, foreign_read, const read_context&, bool fake_context) | |||||
| /* properties */ | /* properties */ | ||||
| @@ -15,11 +15,23 @@ using namespace ::cpphibernate::driver::mariadb_impl; | |||||
| /* data_extractor_t */ | /* data_extractor_t */ | ||||
| struct foreign_many_tuple_t | |||||
| { | |||||
| const field_t& field; | |||||
| read_context_ptr context; | |||||
| std::string owner; | |||||
| }; | |||||
| struct foreign_many_list_t : | |||||
| public std::list<foreign_many_tuple_t> | |||||
| { }; | |||||
| struct data_extractor_t | struct data_extractor_t | ||||
| { | { | ||||
| const table_t& _table; | const table_t& _table; | ||||
| const read_context& _context; | const read_context& _context; | ||||
| const ::cppmariadb::row& _row; | const ::cppmariadb::row& _row; | ||||
| foreign_many_list_t& _foreign_many_list; | |||||
| const filter_t& _filter; | const filter_t& _filter; | ||||
| mutable size_t _index; | mutable size_t _index; | ||||
| @@ -27,14 +39,19 @@ struct data_extractor_t | |||||
| data_extractor_t( | data_extractor_t( | ||||
| const table_t& p_table, | const table_t& p_table, | ||||
| const read_context& p_context, | const read_context& p_context, | ||||
| const ::cppmariadb::row& p_row) | |||||
| : _table (p_table) | |||||
| , _context (p_context) | |||||
| , _row (p_row) | |||||
| , _filter (p_context.filter) | |||||
| , _index (0) | |||||
| const ::cppmariadb::row& p_row, | |||||
| foreign_many_list_t& p_foreign_many_list) | |||||
| : _table (p_table) | |||||
| , _context (p_context) | |||||
| , _row (p_row) | |||||
| , _foreign_many_list(p_foreign_many_list) | |||||
| , _filter (p_context.filter) | |||||
| , _index (0) | |||||
| { } | { } | ||||
| inline bool has_value() const | |||||
| { return !_row.at(_index).is_null(); } | |||||
| inline value_t get_value() const | inline value_t get_value() const | ||||
| { | { | ||||
| value_t ret; | value_t ret; | ||||
| @@ -44,26 +61,67 @@ struct data_extractor_t | |||||
| return ret; | return ret; | ||||
| } | } | ||||
| inline void read_field(const field_t& field, const read_context* context) const | |||||
| inline void next_field() const | |||||
| { ++_index; } | |||||
| inline void read_field(const field_t& field, const read_context& context, bool skip = false) const | |||||
| { | { | ||||
| if (context) | |||||
| { | |||||
| field.set(*context, get_value()); | |||||
| } | |||||
| auto value = get_value(); | |||||
| ++_index; | ++_index; | ||||
| if (!skip) | |||||
| field.set(context, value); | |||||
| } | } | ||||
| inline void read_table(const table_t& table, const read_context* context) const | |||||
| inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const | |||||
| { | { | ||||
| if (table.base_table) | |||||
| read_table(*table.base_table, context); | |||||
| /* read the base table */ | |||||
| if (read_base && table.base_table) | |||||
| { | |||||
| skip = read_table(*table.base_table, context, true, false, skip); | |||||
| } | |||||
| /* create a dynamic dataset depending on the derived table */ | |||||
| else if ( read_base | |||||
| && context.is_dynamic | |||||
| && !table.derived_tables.empty()) | |||||
| { | |||||
| auto value = get_value(); | |||||
| next_field(); | |||||
| if (static_cast<bool>(value) && !skip) | |||||
| { | |||||
| auto type = utl::from_string<uint>(*value); | |||||
| auto derived = _table.get_derived_by_table_id(type); | |||||
| if (!derived) | |||||
| throw misc::hibernate_exception(std::string("unable to find dereived table for id ") + std::to_string(type)); | |||||
| derived->emplace(context); | |||||
| } | |||||
| else | |||||
| { | |||||
| skip = true; | |||||
| } | |||||
| } | |||||
| /* create a static dataset */ | |||||
| else if (has_value() && !skip) | |||||
| { | |||||
| if (read_base) | |||||
| { | |||||
| context.emplace(); | |||||
| } | |||||
| } | |||||
| /* no data -> skip */ | |||||
| else | |||||
| { | |||||
| skip = true; | |||||
| } | |||||
| if (_context.filter.is_excluded(table)) | if (_context.filter.is_excluded(table)) | ||||
| return; | |||||
| return skip; | |||||
| /* primary key */ | /* primary key */ | ||||
| assert(table.primary_key_field); | assert(table.primary_key_field); | ||||
| read_field(*table.primary_key_field, context); | |||||
| read_field(*table.primary_key_field, context, skip); | |||||
| /* data fields */ | /* data fields */ | ||||
| for (auto& ptr : table.data_fields) | for (auto& ptr : table.data_fields) | ||||
| @@ -71,7 +129,7 @@ struct data_extractor_t | |||||
| assert(ptr); | assert(ptr); | ||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| if (!_context.filter.is_excluded(field)) | if (!_context.filter.is_excluded(field)) | ||||
| read_field(field, context); | |||||
| read_field(field, context, skip); | |||||
| } | } | ||||
| /* foreign table one */ | /* foreign table one */ | ||||
| @@ -87,19 +145,54 @@ struct data_extractor_t | |||||
| || _filter.is_excluded(ref_table)) | || _filter.is_excluded(ref_table)) | ||||
| continue; | continue; | ||||
| read_context_ptr next_context; | |||||
| if (context) | |||||
| next_context = field.foreign_read(*context, get_value()); | |||||
| auto next_context = field.foreign_read(context, skip); | |||||
| assert(static_cast<bool>(next_context)); | |||||
| read_table(ref_table, *next_context, true, true, skip); | |||||
| next_context->finish(); | |||||
| } | |||||
| read_table(ref_table, next_context.get()); | |||||
| /* foreign table many */ | |||||
| if (!skip) | |||||
| { | |||||
| for (auto& ptr : table.foreign_table_many_fields) | |||||
| { | |||||
| assert(ptr); | |||||
| assert(ptr->referenced_table); | |||||
| auto& field = *ptr; | |||||
| auto& ref_table = *field.referenced_table; | |||||
| if ( _filter.is_excluded(field) | |||||
| || _filter.is_excluded(ref_table)) | |||||
| continue; | |||||
| _foreign_many_list.emplace_back( | |||||
| foreign_many_tuple_t { | |||||
| field, | |||||
| field.foreign_read(context, false), | |||||
| *table.primary_key_field->get(context), | |||||
| }); | |||||
| } | |||||
| } | } | ||||
| /* derived tables */ | |||||
| if (read_derived && context.is_dynamic) | |||||
| { | |||||
| for (auto& ptr : table.derived_tables) | |||||
| { | |||||
| assert(ptr); | |||||
| auto& derived_table = *ptr; | |||||
| read_table(derived_table, context, false, true, skip); | |||||
| } | |||||
| } | |||||
| return skip; | |||||
| } | } | ||||
| inline void operator()() const | inline void operator()() const | ||||
| { | { | ||||
| _index = 0; | _index = 0; | ||||
| _context.emplace(); | |||||
| read_table(_table, &_context); | |||||
| read_table(_table, _context, true, true, false); | |||||
| if (_index != _row.size()) | if (_index != _row.size()) | ||||
| throw misc::hibernate_exception("result was not completely read!"); | throw misc::hibernate_exception("result was not completely read!"); | ||||
| } | } | ||||
| @@ -109,14 +202,23 @@ struct data_extractor_t | |||||
| struct select_query_builder_t | struct select_query_builder_t | ||||
| { | { | ||||
| const table_t& _table; | |||||
| const filter_t& _filter; | |||||
| bool _is_dynamic; | |||||
| size_t alias_id { 0 }; | |||||
| size_t index { 0 }; | |||||
| std::ostringstream os; | |||||
| std::ostringstream join; | |||||
| struct local_context | |||||
| { | |||||
| const table_t& table; | |||||
| std::string alias; | |||||
| bool add_base; | |||||
| bool add_derived; | |||||
| bool is_dynamic; | |||||
| }; | |||||
| const table_t& _table; | |||||
| const filter_t& _filter; | |||||
| bool _is_dynamic; | |||||
| size_t alias_id { 0 }; | |||||
| size_t index { 0 }; | |||||
| std::ostringstream os; | |||||
| std::list<std::string> joins; | |||||
| select_query_builder_t( | select_query_builder_t( | ||||
| const table_t& p_table, | const table_t& p_table, | ||||
| @@ -127,7 +229,7 @@ struct select_query_builder_t | |||||
| , _is_dynamic(p_is_dynamic) | , _is_dynamic(p_is_dynamic) | ||||
| { } | { } | ||||
| inline std::string make_alias(const table_t& table) | |||||
| inline std::string make_alias() | |||||
| { return std::string("T") + std::to_string(alias_id++); } | { return std::string("T") + std::to_string(alias_id++); } | ||||
| inline void add_field(const field_t& field, const std::string& alias) | inline void add_field(const field_t& field, const std::string& alias) | ||||
| @@ -142,68 +244,87 @@ struct select_query_builder_t | |||||
| << field.convert_from_close; | << field.convert_from_close; | ||||
| } | } | ||||
| inline bool add_table(const table_t& table, const std::string& alias) | |||||
| inline bool add_table(const local_context& ctx) | |||||
| { | { | ||||
| bool ret = false; | |||||
| bool ret = false; | |||||
| auto has_alias = !ctx.alias.empty(); | |||||
| auto real_alias = has_alias ? ctx.alias : ctx.table.table_name; | |||||
| if (table.base_table) | |||||
| if (ctx.table.base_table && ctx.add_base) | |||||
| { | { | ||||
| auto& base_table = *table.base_table; | |||||
| auto base_alias = make_alias(base_table); | |||||
| auto tmp = add_table(base_table, base_alias); | |||||
| if (tmp) | |||||
| assert(ctx.table.base_table->primary_key_field); | |||||
| auto& base_table = *ctx.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; | |||||
| std::ostringstream ss; | |||||
| ss << " JOIN `" | |||||
| << base_table.table_name; | |||||
| if (has_alias) | |||||
| { | |||||
| ss << "` AS `" | |||||
| << base_alias; | |||||
| } | |||||
| ss << "` ON `" | |||||
| << real_alias | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`=`" | |||||
| << real_base_alias | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`"; | |||||
| auto it = joins.insert(joins.end(), ss.str()); | |||||
| if (add_table({ | |||||
| base_table, | |||||
| base_alias, | |||||
| true, | |||||
| false, | |||||
| ctx.is_dynamic | |||||
| })) | |||||
| { | { | ||||
| assert(base_table.primary_key_field); | |||||
| auto& base_key = *base_table.primary_key_field; | |||||
| ret = true; | ret = true; | ||||
| join << " LEFT JOIN `" | |||||
| << base_table.table_name | |||||
| << "` AS `" | |||||
| << base_alias | |||||
| << "` ON `" | |||||
| << alias | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`=`" | |||||
| << base_alias | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`"; | |||||
| } | |||||
| else | |||||
| { | |||||
| joins.erase(it); | |||||
| } | } | ||||
| } | } | ||||
| /* __type */ | /* __type */ | ||||
| if ( _is_dynamic | |||||
| && !table.base_table | |||||
| && !table.derived_tables.empty()) | |||||
| if ( ctx.is_dynamic | |||||
| && !ctx.table.base_table | |||||
| && !ctx.table.derived_tables.empty()) | |||||
| { | { | ||||
| if (index++) os << ", "; | if (index++) os << ", "; | ||||
| os << "`" | os << "`" | ||||
| << table.table_name | |||||
| << ctx.table.table_name | |||||
| << "`.`__type` AS `__type`"; | << "`.`__type` AS `__type`"; | ||||
| ret = true; | ret = true; | ||||
| } | } | ||||
| if (_filter.is_excluded(table)) | |||||
| if (_filter.is_excluded(ctx.table)) | |||||
| return ret; | return ret; | ||||
| ret = true; | ret = true; | ||||
| /* primary key */ | /* primary key */ | ||||
| assert(table.primary_key_field); | |||||
| add_field(*table.primary_key_field, alias); | |||||
| assert(ctx.table.primary_key_field); | |||||
| add_field(*ctx.table.primary_key_field, real_alias); | |||||
| /* data fields */ | /* data fields */ | ||||
| for (auto& ptr : table.data_fields) | |||||
| for (auto& ptr : ctx.table.data_fields) | |||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| auto& field = *ptr; | auto& field = *ptr; | ||||
| if (!_filter.is_excluded(field)) | if (!_filter.is_excluded(field)) | ||||
| add_field(field, alias); | |||||
| add_field(field, real_alias); | |||||
| } | } | ||||
| /* foreign table one */ | /* foreign table one */ | ||||
| for (auto& ptr : table.foreign_table_one_fields) | |||||
| for (auto& ptr : ctx.table.foreign_table_one_fields) | |||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| assert(ptr->referenced_table); | assert(ptr->referenced_table); | ||||
| @@ -217,23 +338,84 @@ struct select_query_builder_t | |||||
| || _filter.is_excluded(ref_table)) | || _filter.is_excluded(ref_table)) | ||||
| continue; | continue; | ||||
| auto new_alias = make_alias(ref_table); | |||||
| add_table(ref_table, new_alias); | |||||
| join << " LEFT JOIN `" | |||||
| << ref_table.table_name | |||||
| << "` AS `" | |||||
| << new_alias | |||||
| << "` ON `" | |||||
| << alias | |||||
| auto new_alias = make_alias(); | |||||
| 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 | |||||
| << "`"; | |||||
| auto it = joins.insert(joins.end(), ss.str()); | |||||
| if (!add_table({ | |||||
| ref_table, | |||||
| new_alias, | |||||
| true, | |||||
| true, | |||||
| field.value_is_pointer | |||||
| })) | |||||
| { | |||||
| joins.erase(it); | |||||
| } | |||||
| } | |||||
| /* derived tables */ | |||||
| if (ctx.add_derived && ctx.is_dynamic) | |||||
| { | |||||
| for (auto& ptr : ctx.table.derived_tables) | |||||
| { | |||||
| assert(ptr); | |||||
| assert(ptr->primary_key_field); | |||||
| auto& derived_table = *ptr; | |||||
| auto& primary_key = *ctx.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; | |||||
| std::ostringstream ss; | |||||
| ss << " LEFT JOIN `" | |||||
| << derived_table.table_name; | |||||
| if (has_alias) | |||||
| { | |||||
| ss << "` AS `" | |||||
| << derived_alias; | |||||
| } | |||||
| ss << "` ON `" | |||||
| << real_alias | |||||
| << "`.`" | << "`.`" | ||||
| << ref_key.table_name | |||||
| << "_id_" | |||||
| << field.field_name | |||||
| << primary_key.field_name | |||||
| << "`=`" | << "`=`" | ||||
| << new_alias | |||||
| << real_derived_alias | |||||
| << "`.`" | << "`.`" | ||||
| << ref_key.field_name | |||||
| << primary_key.field_name | |||||
| << "`"; | << "`"; | ||||
| auto it = joins.insert(joins.end(), ss.str()); | |||||
| if (add_table({ | |||||
| derived_table, | |||||
| derived_alias, | |||||
| false, | |||||
| true, | |||||
| ctx.is_dynamic, | |||||
| })) | |||||
| { | |||||
| ret = true; | |||||
| } | |||||
| else | |||||
| { | |||||
| joins.erase(it); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| return ret; | return ret; | ||||
| @@ -242,15 +424,21 @@ struct select_query_builder_t | |||||
| inline std::string operator()() | inline std::string operator()() | ||||
| { | { | ||||
| os << "SELECT "; | os << "SELECT "; | ||||
| auto alias = make_alias(_table); | |||||
| add_table(_table, alias); | |||||
| add_table({ | |||||
| _table, | |||||
| "", | |||||
| true, | |||||
| true, | |||||
| _is_dynamic, | |||||
| }); | |||||
| os << " FROM `" | os << " FROM `" | ||||
| << _table.table_name | << _table.table_name | ||||
| << "` AS `" | |||||
| << alias | |||||
| << "`" | |||||
| << join.str() | |||||
| << " ?where! ?order! ?limit!"; | |||||
| << "`"; | |||||
| for (auto& join : joins) | |||||
| os << join; | |||||
| os << " ?where! ?order! ?limit!"; | |||||
| return os.str(); | return os.str(); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -958,19 +1146,35 @@ void table_t::print(std::ostream& os) const | |||||
| << indent << '}'; | << indent << '}'; | ||||
| } | } | ||||
| const table_t* table_t::get_derived(size_t id) const | |||||
| const table_t* table_t::get_derived_by_table_id(size_t id) const | |||||
| { | |||||
| if (table_id == id) | |||||
| return this; | |||||
| for (auto ptr : derived_tables) | |||||
| { | |||||
| assert(ptr); | |||||
| auto ret = ptr->get_derived_by_table_id(id); | |||||
| if (ret) return ret; | |||||
| } | |||||
| return nullptr; | |||||
| } | |||||
| const table_t* table_t::get_derived_by_dataset_id(size_t id) const | |||||
| { | { | ||||
| if (dataset_id == id) | if (dataset_id == id) | ||||
| return this; | return this; | ||||
| for (auto ptr : derived_tables) | for (auto ptr : derived_tables) | ||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| auto ret = ptr->get_derived(id); | |||||
| auto ret = ptr->get_derived_by_dataset_id(id); | |||||
| if (ret) return ret; | if (ret) return ret; | ||||
| } | } | ||||
| return nullptr; | return nullptr; | ||||
| } | } | ||||
| void table_t::emplace(const read_context& context) const | |||||
| { throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); } | |||||
| ::cppmariadb::statement& table_t::get_statement_create_table() const | ::cppmariadb::statement& table_t::get_statement_create_table() const | ||||
| { | { | ||||
| if (_statement_create_table) | if (_statement_create_table) | ||||
| @@ -1051,13 +1255,47 @@ void table_t::read_exec(const read_context& context) const | |||||
| { | { | ||||
| auto& statement = get_statement_select(context); | auto& statement = get_statement_select(context); | ||||
| auto& connection = context.connection; | auto& connection = context.connection; | ||||
| statement.set(0, context.where); | |||||
| statement.set(1, context.order_by); | |||||
| statement.set(2, context.limit); | |||||
| cpphibernate_debug_log("execute SELECT query: " << statement.query(connection)); | cpphibernate_debug_log("execute SELECT query: " << statement.query(connection)); | ||||
| auto result = connection.execute_used(statement); | auto result = connection.execute_used(statement); | ||||
| if (!result) | if (!result) | ||||
| throw misc::hibernate_exception("Unable to fetching data from database!"); | throw misc::hibernate_exception("Unable to fetching data from database!"); | ||||
| ::cppmariadb::row* row; | ::cppmariadb::row* row; | ||||
| foreign_many_list_t foreign_many_list; | |||||
| while ((row = result->next())) | while ((row = result->next())) | ||||
| data_extractor_t(*this, context, *row)(); | |||||
| data_extractor_t(*this, context, *row, foreign_many_list)(); | |||||
| context.finish(); | context.finish(); | ||||
| for (auto& tuple : foreign_many_list) | |||||
| { | |||||
| auto& field = tuple.field; | |||||
| auto& next_context = *tuple.context; | |||||
| assert(field.table); | |||||
| assert(field.referenced_table); | |||||
| assert(field.referenced_table->primary_key_field); | |||||
| auto& ref_table = *field.referenced_table; | |||||
| auto& ref_field = *ref_table.primary_key_field; | |||||
| std::ostringstream ss; | |||||
| ss << "WHERE (`" | |||||
| << ref_table.table_name | |||||
| << "`.`" | |||||
| << field.table_name | |||||
| << "_id_" | |||||
| << field.field_name | |||||
| << "`=" | |||||
| << ref_field.convert_to_open | |||||
| << "'" | |||||
| << context.connection.escape(tuple.owner) | |||||
| << "'" | |||||
| << ref_field.convert_to_close | |||||
| << ")"; | |||||
| next_context.where = ss.str(); | |||||
| ref_table.read(next_context); | |||||
| } | |||||
| } | } | ||||
| @@ -15,14 +15,16 @@ TEST(CppHibernateTests, read_test1) | |||||
| expect_query(mock, "START TRANSACTION"); | expect_query(mock, "START TRANSACTION"); | ||||
| expect_query(mock, "SELECT " | expect_query(mock, "SELECT " | ||||
| "BinToUuid(`T0`.`tbl_test1_id`), " | |||||
| "`T0`.`str_data`, " | |||||
| "`T0`.`str64_data`, " | |||||
| "`T0`.`u32_nullable`, " | |||||
| "`T0`.`u32_ptr_u`, " | |||||
| "`T0`.`u32_ptr_s` " | |||||
| "BinToUuid(`tbl_test1`.`tbl_test1_id`), " | |||||
| "`tbl_test1`.`str_data`, " | |||||
| "`tbl_test1`.`str64_data`, " | |||||
| "`tbl_test1`.`u32_nullable`, " | |||||
| "`tbl_test1`.`u32_ptr_u`, " | |||||
| "`tbl_test1`.`u32_ptr_s` " | |||||
| "FROM " | "FROM " | ||||
| "`tbl_test1` AS `T0` ", | |||||
| "`tbl_test1` " | |||||
| "WHERE " | |||||
| "(`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | result_used({ | ||||
| { "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" } | { "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" } | ||||
| })); | })); | ||||
| @@ -64,13 +66,15 @@ TEST(CppHibernateTests, read_test2) | |||||
| expect_query(mock, "START TRANSACTION"); | expect_query(mock, "START TRANSACTION"); | ||||
| expect_query(mock, "SELECT " | expect_query(mock, "SELECT " | ||||
| "BinToUuid(`T0`.`tbl_test2_id`), " | |||||
| "`T0`.`u8_data`, " | |||||
| "`T0`.`i8_data`, " | |||||
| "`T0`.`u16_data`, " | |||||
| "`T0`.`i16_data` " | |||||
| "BinToUuid(`tbl_test2`.`tbl_test2_id`), " | |||||
| "`tbl_test2`.`u8_data`, " | |||||
| "`tbl_test2`.`i8_data`, " | |||||
| "`tbl_test2`.`u16_data`, " | |||||
| "`tbl_test2`.`i16_data` " | |||||
| "FROM " | "FROM " | ||||
| "`tbl_test2` AS `T0` ", | |||||
| "`tbl_test2` " | |||||
| "WHERE " | |||||
| "(`tbl_test2`.`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | result_used({ | ||||
| { "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" } | { "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" } | ||||
| })); | })); | ||||
| @@ -108,13 +112,15 @@ TEST(CppHibernateTests, read_test3) | |||||
| expect_query(mock, "START TRANSACTION"); | expect_query(mock, "START TRANSACTION"); | ||||
| expect_query(mock, "SELECT " | expect_query(mock, "SELECT " | ||||
| "BinToUuid(`T0`.`tbl_test3_id`), " | |||||
| "`T0`.`u32_data`, " | |||||
| "`T0`.`i32_data`, " | |||||
| "`T0`.`u64_data`, " | |||||
| "`T0`.`i64_data` " | |||||
| "BinToUuid(`tbl_test3`.`tbl_test3_id`), " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | "FROM " | ||||
| "`tbl_test3` AS `T0` ", | |||||
| "`tbl_test3` " | |||||
| "WHERE " | |||||
| "(`tbl_test3`.`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | result_used({ | ||||
| { "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" } | { "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" } | ||||
| })); | })); | ||||
| @@ -152,22 +158,24 @@ TEST(CppHibernateTests, read_derived1_static) | |||||
| expect_query(mock, "START TRANSACTION"); | expect_query(mock, "START TRANSACTION"); | ||||
| expect_query(mock, "SELECT " | expect_query(mock, "SELECT " | ||||
| "BinToUuid(`T1`.`tbl_base_id`), " | |||||
| "`T1`.`name`, " | |||||
| "BinToUuid(`T0`.`tbl_derived1_id`), " | |||||
| "`T0`.`enum_data`, " | |||||
| "BinToUuid(`T2`.`tbl_test1_id`), " | |||||
| "`T2`.`str_data`, " | |||||
| "`T2`.`str64_data`, " | |||||
| "`T2`.`u32_nullable`, " | |||||
| "`T2`.`u32_ptr_u`, " | |||||
| "`T2`.`u32_ptr_s` " | |||||
| "BinToUuid(`tbl_base`.`tbl_base_id`), " | |||||
| "`tbl_base`.`name`, " | |||||
| "BinToUuid(`tbl_derived1`.`tbl_derived1_id`), " | |||||
| "`tbl_derived1`.`enum_data`, " | |||||
| "BinToUuid(`T0`.`tbl_test1_id`), " | |||||
| "`T0`.`str_data`, " | |||||
| "`T0`.`str64_data`, " | |||||
| "`T0`.`u32_nullable`, " | |||||
| "`T0`.`u32_ptr_u`, " | |||||
| "`T0`.`u32_ptr_s` " | |||||
| "FROM " | "FROM " | ||||
| "`tbl_derived1` AS `T0` " | |||||
| "LEFT JOIN " | |||||
| "`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` " | |||||
| "`tbl_derived1` " | |||||
| "JOIN " | |||||
| "`tbl_base` ON `tbl_derived1`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||||
| "LEFT JOIN " | "LEFT JOIN " | ||||
| "`tbl_test1` AS `T2` ON `T0`.`tbl_test1_id_test1_data`=`T2`.`tbl_test1_id` ", | |||||
| "`tbl_test1` AS `T0` ON `tbl_derived1`.`tbl_test1_id_test1_data`=`T0`.`tbl_test1_id` " | |||||
| "WHERE " | |||||
| "(`tbl_derived1`.`tbl_derived1_id`=UuidToBin('X3d12758c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | result_used({ | ||||
| { "3d12778a-abb9-11e8-98d0-529269fb1459", "derived1", "3d12758c-abb9-11e8-98d0-529269fb1459", "test2", "3d127988-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `d1.test1_data`", "str64_data of class `test1` object `d1.test1_data`", "32", nullptr, "789" } | { "3d12778a-abb9-11e8-98d0-529269fb1459", "derived1", "3d12758c-abb9-11e8-98d0-529269fb1459", "test2", "3d127988-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `d1.test1_data`", "str64_data of class `test1` object `d1.test1_data`", "32", nullptr, "789" } | ||||
| })); | })); | ||||
| @@ -211,34 +219,36 @@ TEST(CppHibernateTests, read_derived2_static) | |||||
| expect_query(mock, "START TRANSACTION"); | expect_query(mock, "START TRANSACTION"); | ||||
| expect_query(mock, "SELECT " | expect_query(mock, "SELECT " | ||||
| "BinToUuid(`T1`.`tbl_base_id`), " | |||||
| "`T1`.`name`, " | |||||
| "BinToUuid(`T0`.`tbl_derived2_id`), " | |||||
| "BinToUuid(`tbl_base`.`tbl_base_id`), " | |||||
| "`tbl_base`.`name`, " | |||||
| "BinToUuid(`tbl_derived2`.`tbl_derived2_id`), " | |||||
| "BinToUuid(`T0`.`tbl_test2_id`), " | |||||
| "`T0`.`u8_data`, " | |||||
| "`T0`.`i8_data`, " | |||||
| "`T0`.`u16_data`, " | |||||
| "`T0`.`i16_data`, " | |||||
| "BinToUuid(`T1`.`tbl_test2_id`), " | |||||
| "`T1`.`u8_data`, " | |||||
| "`T1`.`i8_data`, " | |||||
| "`T1`.`u16_data`, " | |||||
| "`T1`.`i16_data`, " | |||||
| "BinToUuid(`T2`.`tbl_test2_id`), " | "BinToUuid(`T2`.`tbl_test2_id`), " | ||||
| "`T2`.`u8_data`, " | "`T2`.`u8_data`, " | ||||
| "`T2`.`i8_data`, " | "`T2`.`i8_data`, " | ||||
| "`T2`.`u16_data`, " | "`T2`.`u16_data`, " | ||||
| "`T2`.`i16_data`, " | |||||
| "BinToUuid(`T3`.`tbl_test2_id`), " | |||||
| "`T3`.`u8_data`, " | |||||
| "`T3`.`i8_data`, " | |||||
| "`T3`.`u16_data`, " | |||||
| "`T3`.`i16_data`, " | |||||
| "BinToUuid(`T4`.`tbl_test2_id`), " | |||||
| "`T4`.`u8_data`, " | |||||
| "`T4`.`i8_data`, " | |||||
| "`T4`.`u16_data`, " | |||||
| "`T4`.`i16_data` " | |||||
| "`T2`.`i16_data` " | |||||
| "FROM " | "FROM " | ||||
| "`tbl_derived2` AS `T0` " | |||||
| "`tbl_derived2` " | |||||
| "JOIN " | |||||
| "`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " | |||||
| "LEFT JOIN " | "LEFT JOIN " | ||||
| "`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` " | |||||
| "`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` " | |||||
| "LEFT JOIN " | "LEFT JOIN " | ||||
| "`tbl_test2` AS `T2` ON `T0`.`tbl_test2_id_test2_nullable`=`T2`.`tbl_test2_id` " | |||||
| "`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` " | |||||
| "LEFT JOIN " | "LEFT JOIN " | ||||
| "`tbl_test2` AS `T3` ON `T0`.`tbl_test2_id_test2_ptr_u`=`T3`.`tbl_test2_id` " | |||||
| "LEFT JOIN " | |||||
| "`tbl_test2` AS `T4` ON `T0`.`tbl_test2_id_test2_ptr_s`=`T4`.`tbl_test2_id` ", | |||||
| "`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` " | |||||
| "WHERE " | |||||
| "(`tbl_derived2`.`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | result_used({ | ||||
| { "3d127db6-abb9-11e8-98d0-529269fb1459", "derived2", "3d127bcc-abb9-11e8-98d0-529269fb1459", "3d1283a6-abb9-11e8-98d0-529269fb1459", "10", "11", "12", "13", "3d128522-abb9-11e8-98d0-529269fb1459", "20", "21", "22", "23", nullptr, nullptr, nullptr, nullptr, nullptr } | { "3d127db6-abb9-11e8-98d0-529269fb1459", "derived2", "3d127bcc-abb9-11e8-98d0-529269fb1459", "3d1283a6-abb9-11e8-98d0-529269fb1459", "10", "11", "12", "13", "3d128522-abb9-11e8-98d0-529269fb1459", "20", "21", "22", "23", nullptr, nullptr, nullptr, nullptr, nullptr } | ||||
| })); | })); | ||||
| @@ -278,3 +288,315 @@ TEST(CppHibernateTests, read_derived2_static) | |||||
| EXPECT_EQ (d2.test2_ptr_u->i16_data, 23); | EXPECT_EQ (d2.test2_ptr_u->i16_data, 23); | ||||
| EXPECT_FALSE(static_cast<bool>(d2.test2_ptr_s)); | EXPECT_FALSE(static_cast<bool>(d2.test2_ptr_s)); | ||||
| } | } | ||||
| TEST(CppHibernateTests, read_derived3_static) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_base`.`tbl_base_id`), " | |||||
| "`tbl_base`.`name`, " | |||||
| "BinToUuid(`tbl_derived2`.`tbl_derived2_id`), " | |||||
| "BinToUuid(`T0`.`tbl_test2_id`), " | |||||
| "`T0`.`u8_data`, " | |||||
| "`T0`.`i8_data`, " | |||||
| "`T0`.`u16_data`, " | |||||
| "`T0`.`i16_data`, " | |||||
| "BinToUuid(`T1`.`tbl_test2_id`), " | |||||
| "`T1`.`u8_data`, " | |||||
| "`T1`.`i8_data`, " | |||||
| "`T1`.`u16_data`, " | |||||
| "`T1`.`i16_data`, " | |||||
| "BinToUuid(`T2`.`tbl_test2_id`), " | |||||
| "`T2`.`u8_data`, " | |||||
| "`T2`.`i8_data`, " | |||||
| "`T2`.`u16_data`, " | |||||
| "`T2`.`i16_data`, " | |||||
| "BinToUuid(`tbl_derived3`.`tbl_derived3_id`) " | |||||
| "FROM " | |||||
| "`tbl_derived3` " | |||||
| "JOIN " | |||||
| "`tbl_derived2` ON `tbl_derived3`.`tbl_derived2_id`=`tbl_derived2`.`tbl_derived2_id` " | |||||
| "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_derived3`.`tbl_derived3_id`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { "3d1288ce-abb9-11e8-98d0-529269fb1459", "derived3", "3d1287a2-abb9-11e8-98d0-529269fb1459", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "3d12866c-abb9-11e8-98d0-529269fb1459" } | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_test3`.`tbl_test3_id`), " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | |||||
| "`tbl_test3` " | |||||
| "WHERE " | |||||
| "(`tbl_test3`.`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { "3d1289f0-abb9-11e8-98d0-529269fb1459", "100", "101", "102", "103" }, | |||||
| { "3d128b26-abb9-11e8-98d0-529269fb1459", "110", "111", "112", "113" }, | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_test3`.`tbl_test3_id`), " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | |||||
| "`tbl_test3` " | |||||
| "WHERE " | |||||
| "(`tbl_test3`.`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { "3d128eb4-abb9-11e8-98d0-529269fb1459", "120", "121", "122", "123" }, | |||||
| { "3d128ffe-abb9-11e8-98d0-529269fb1459", "130", "131", "132", "133" }, | |||||
| { "3d129134-abb9-11e8-98d0-529269fb1459", "140", "141", "142", "143" }, | |||||
| })); | |||||
| 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))); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| derived3 d3; | |||||
| d3.derived3_id = uuid("3d12866c-abb9-11e8-98d0-529269fb1459"); | |||||
| context.read(d3); | |||||
| EXPECT_EQ (d3.id, uuid("3d1288ce-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ (d3.name, "derived3"); | |||||
| EXPECT_EQ (d3.derived2_id, uuid("3d1287a2-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_nullable)); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_u)); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_s)); | |||||
| EXPECT_EQ (d3.derived3_id, uuid("3d12866c-abb9-11e8-98d0-529269fb1459")); | |||||
| { | |||||
| auto it = d3.test3_list.begin(); | |||||
| ASSERT_NE(it, d3.test3_list.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d1289f0-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 100); | |||||
| EXPECT_EQ(it->i32_data, 101); | |||||
| EXPECT_EQ(it->u64_data, 102); | |||||
| EXPECT_EQ(it->i64_data, 103); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_list.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128b26-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 110); | |||||
| EXPECT_EQ(it->i32_data, 111); | |||||
| EXPECT_EQ(it->u64_data, 112); | |||||
| EXPECT_EQ(it->i64_data, 113); | |||||
| ++it; | |||||
| EXPECT_EQ(it, d3.test3_list.end()); | |||||
| } | |||||
| { | |||||
| auto it = d3.test3_vector.begin(); | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128eb4-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 120); | |||||
| EXPECT_EQ(it->i32_data, 121); | |||||
| EXPECT_EQ(it->u64_data, 122); | |||||
| EXPECT_EQ(it->i64_data, 123); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128ffe-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 130); | |||||
| EXPECT_EQ(it->i32_data, 131); | |||||
| EXPECT_EQ(it->u64_data, 132); | |||||
| EXPECT_EQ(it->i64_data, 133); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d129134-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 140); | |||||
| EXPECT_EQ(it->i32_data, 141); | |||||
| EXPECT_EQ(it->u64_data, 142); | |||||
| EXPECT_EQ(it->i64_data, 143); | |||||
| ++it; | |||||
| EXPECT_EQ(it, d3.test3_vector.end()); | |||||
| } | |||||
| } | |||||
| TEST(CppHibernateTests, read_base_ptr_dynamic) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "`tbl_base`.`__type` AS `__type`, " | |||||
| "BinToUuid(`tbl_base`.`tbl_base_id`), " | |||||
| "`tbl_base`.`name`, " | |||||
| "BinToUuid(`tbl_derived2`.`tbl_derived2_id`), " | |||||
| "BinToUuid(`T0`.`tbl_test2_id`), " | |||||
| "`T0`.`u8_data`, " | |||||
| "`T0`.`i8_data`, " | |||||
| "`T0`.`u16_data`, " | |||||
| "`T0`.`i16_data`, " | |||||
| "BinToUuid(`T1`.`tbl_test2_id`), " | |||||
| "`T1`.`u8_data`, " | |||||
| "`T1`.`i8_data`, " | |||||
| "`T1`.`u16_data`, " | |||||
| "`T1`.`i16_data`, " | |||||
| "BinToUuid(`T2`.`tbl_test2_id`), " | |||||
| "`T2`.`u8_data`, " | |||||
| "`T2`.`i8_data`, " | |||||
| "`T2`.`u16_data`, " | |||||
| "`T2`.`i16_data`, " | |||||
| "BinToUuid(`tbl_derived3`.`tbl_derived3_id`) " | |||||
| "FROM " | |||||
| "`tbl_derived2` " | |||||
| "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_derived2`.`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { | |||||
| /* base */ "13", "3d1288ce-abb9-11e8-98d0-529269fb1459", "derived3", | |||||
| /* derived2 */ "3d1287a2-abb9-11e8-98d0-529269fb1459", | |||||
| /* derived2.test2_nullable */ nullptr, nullptr, nullptr, nullptr, nullptr, | |||||
| /* derived2.test2_ptr_u */ nullptr, nullptr, nullptr, nullptr, nullptr, | |||||
| /* derived2.test2_ptr_s */ nullptr, nullptr, nullptr, nullptr, nullptr, | |||||
| /* derived3 */ "3d12866c-abb9-11e8-98d0-529269fb1459" | |||||
| } | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_test3`.`tbl_test3_id`), " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | |||||
| "`tbl_test3` " | |||||
| "WHERE " | |||||
| "(`tbl_test3`.`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { "3d1289f0-abb9-11e8-98d0-529269fb1459", "100", "101", "102", "103" }, | |||||
| { "3d128b26-abb9-11e8-98d0-529269fb1459", "110", "111", "112", "113" }, | |||||
| })); | |||||
| expect_query(mock, "SELECT " | |||||
| "BinToUuid(`tbl_test3`.`tbl_test3_id`), " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | |||||
| "`tbl_test3` " | |||||
| "WHERE " | |||||
| "(`tbl_test3`.`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ", | |||||
| result_used({ | |||||
| { "3d128eb4-abb9-11e8-98d0-529269fb1459", "120", "121", "122", "123" }, | |||||
| { "3d128ffe-abb9-11e8-98d0-529269fb1459", "130", "131", "132", "133" }, | |||||
| { "3d129134-abb9-11e8-98d0-529269fb1459", "140", "141", "142", "143" }, | |||||
| })); | |||||
| 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))); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||||
| constexpr decltype(auto) d2_key_field = test_schema.tables[5_c].fields[0_c]; | |||||
| std::unique_ptr<derived2> d2_ptr; | |||||
| context.read(d2_ptr, where(equal(d2_key_field, "3d1287a2-abb9-11e8-98d0-529269fb1459"))); | |||||
| auto* d3_ptr = dynamic_cast<derived3*>(d2_ptr.get()); | |||||
| ASSERT_TRUE (d3_ptr); | |||||
| auto& d3 = *d3_ptr; | |||||
| EXPECT_EQ (d3.id, uuid("3d1288ce-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ (d3.name, "derived3"); | |||||
| EXPECT_EQ (d3.derived2_id, uuid("3d1287a2-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_nullable)); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_u)); | |||||
| EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_s)); | |||||
| EXPECT_EQ (d3.derived3_id, uuid("3d12866c-abb9-11e8-98d0-529269fb1459")); | |||||
| { | |||||
| auto it = d3.test3_list.begin(); | |||||
| ASSERT_NE(it, d3.test3_list.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d1289f0-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 100); | |||||
| EXPECT_EQ(it->i32_data, 101); | |||||
| EXPECT_EQ(it->u64_data, 102); | |||||
| EXPECT_EQ(it->i64_data, 103); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_list.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128b26-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 110); | |||||
| EXPECT_EQ(it->i32_data, 111); | |||||
| EXPECT_EQ(it->u64_data, 112); | |||||
| EXPECT_EQ(it->i64_data, 113); | |||||
| ++it; | |||||
| EXPECT_EQ(it, d3.test3_list.end()); | |||||
| } | |||||
| { | |||||
| auto it = d3.test3_vector.begin(); | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128eb4-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 120); | |||||
| EXPECT_EQ(it->i32_data, 121); | |||||
| EXPECT_EQ(it->u64_data, 122); | |||||
| EXPECT_EQ(it->i64_data, 123); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d128ffe-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 130); | |||||
| EXPECT_EQ(it->i32_data, 131); | |||||
| EXPECT_EQ(it->u64_data, 132); | |||||
| EXPECT_EQ(it->i64_data, 133); | |||||
| ++it; | |||||
| ASSERT_NE(it, d3.test3_vector.end()); | |||||
| EXPECT_EQ(it->id, uuid("3d129134-abb9-11e8-98d0-529269fb1459")); | |||||
| EXPECT_EQ(it->u32_data, 140); | |||||
| EXPECT_EQ(it->i32_data, 141); | |||||
| EXPECT_EQ(it->u64_data, 142); | |||||
| EXPECT_EQ(it->i64_data, 143); | |||||
| ++it; | |||||
| EXPECT_EQ(it, d3.test3_vector.end()); | |||||
| } | |||||
| } | |||||