From f556a98db36dbe4813eaffc461fed7b07cb077ca Mon Sep 17 00:00:00 2001 From: bergmann Date: Thu, 13 Sep 2018 20:47:24 +0200 Subject: [PATCH] * refactored read methods for mariadb driver * implemented read methods of foreign many tables for mariadb driver --- .../driver/mariadb/helper/context.h | 60 ++- .../driver/mariadb/helper/context.inl | 66 ++- .../cpphibernate/driver/mariadb/impl/read.h | 265 ++++++++--- .../cpphibernate/driver/mariadb/impl/where.h | 1 - include/cpphibernate/driver/mariadb/mariadb.h | 12 +- .../driver/mariadb/schema/field.fwd.h | 2 +- .../driver/mariadb/schema/field.h | 6 +- .../driver/mariadb/schema/field.inl | 51 +-- .../driver/mariadb/schema/table.fwd.h | 2 +- .../driver/mariadb/schema/table.h | 8 +- .../driver/mariadb/schema/table.inl | 9 +- include/cpphibernate/misc.h | 1 + include/cpphibernate/misc/general.h | 2 + include/cpphibernate/misc/nullable_helper.h | 23 +- src/driver/mariadb/schema/field.cpp | 3 +- src/driver/mariadb/schema/table.cpp | 412 +++++++++++++---- test/cpphibernate_read.cpp | 428 +++++++++++++++--- 17 files changed, 1062 insertions(+), 289 deletions(-) diff --git a/include/cpphibernate/driver/mariadb/helper/context.h b/include/cpphibernate/driver/mariadb/helper/context.h index 85fb45e..0e1a536 100644 --- a/include/cpphibernate/driver/mariadb/helper/context.h +++ b/include/cpphibernate/driver/mariadb/helper/context.h @@ -50,9 +50,8 @@ beg_namespace_cpphibernate_driver_mariadb template 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>); - new_context.data = &data; + auto new_context = context; + new_context.set(data); return new_context; } }; @@ -68,12 +67,26 @@ beg_namespace_cpphibernate_driver_mariadb private: 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 + inline void* set(T_dataset& dataset, size_t dataset_id = 0) const; public: - template - inline decltype(auto) get(const table_t* table = nullptr) const; + template + 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 inline data_context( @@ -81,8 +94,9 @@ beg_namespace_cpphibernate_driver_mariadb const schema_t& p_schema, ::cppmariadb::connection& p_connection) : base_context (p_schema, p_connection) - , data_id (misc::get_type_id(hana::type_c>)) - , data (&p_data) + , _dataset_id (misc::get_type_id(hana::type_c>)) + , _dataset (&p_data) + , _table (nullptr) { } }; @@ -93,6 +107,14 @@ beg_namespace_cpphibernate_driver_mariadb { 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 inline filter_context( T_data& p_data, @@ -135,40 +157,40 @@ beg_namespace_cpphibernate_driver_mariadb struct read_context : public filter_context { - bool is_dynamic; + protected: size_t base_dataset_id; + + public: + bool is_dynamic; std::string where; std::string limit; std::string order_by; - template + protected: inline read_context( - T_data& p_data, const schema_t& p_schema, ::cppmariadb::connection& p_connection, 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) , base_dataset_id (0) { } + public: virtual ~read_context() = default; template inline T_dataset& emplace(const table_t* table = nullptr) const; void emplace() const - { emplace_intern(nullptr); } + { emplace_intern(nullptr, 0); } void finish() const { finish_intern(); } 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; diff --git a/include/cpphibernate/driver/mariadb/helper/context.inl b/include/cpphibernate/driver/mariadb/helper/context.inl index af6ac96..cbdb675 100644 --- a/include/cpphibernate/driver/mariadb/helper/context.inl +++ b/include/cpphibernate/driver/mariadb/helper/context.inl @@ -7,29 +7,50 @@ beg_namespace_cpphibernate_driver_mariadb /* data_context */ - template + template + inline void* data_context + ::set(T_dataset& dataset, size_t dataset_id) const + { + using dataset_type = mp::decay_t; + + _table = nullptr; + _dataset = &dataset; + _dataset_id = (dataset_id == 0) + ? misc::get_type_id(hana::type_c) + : dataset_id; + + return _dataset; + } + + template inline decltype(auto) data_context - ::get(const table_t* table) const + ::get() const { - if (!data) + using dataset_type = mp::decay_t; + + if (!_dataset) throw misc::hibernate_exception("no data assigned!"); - auto type_id = misc::get_type_id(hana::type_c>); - if (type_id != data_id) + auto dataset_id = misc::get_type_id(hana::type_c); + 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; if (!table) { - throw misc::hibernate_exception(static_cast(std::ostringstream { } - << "invalid type! expected " << data_id << ", got " << type_id).str()); + throw misc::hibernate_exception(utl::type_helper::name() + + " is not a derived type of dataset with id " + std::to_string(_dataset_id)); } } - return *static_cast(data); + return *static_cast(_dataset); } /* read_context */ @@ -38,29 +59,32 @@ beg_namespace_cpphibernate_driver_mariadb T_dataset& read_context ::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; // check table - auto dataset_id = misc::get_type_id(hana::type_c>); + auto dataset_id = misc::get_type_id(hana::type_c); if (!table) table = &schema.table(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 auto tbl = table; while (tbl && tbl->dataset_id != base_dataset_id) tbl = tbl->base_table; if (!tbl) - throw misc::hibernate_exception(utl::type_helper::name() + " is not a derived type of table " + table->table_name); + { + throw misc::hibernate_exception(utl::type_helper::name() + + " is not a derived type of dataset with id " + std::to_string(base_dataset_id)); + } // create dataset - auto ptr = std::make_unique(); - auto ret = emplace_intern(ptr.get()); - if (!ret) - ret = ptr.release(); - return *static_cast(ret); + auto ptr = std::make_unique(); + 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(data); } } diff --git a/include/cpphibernate/driver/mariadb/impl/read.h b/include/cpphibernate/driver/mariadb/impl/read.h index bd8143c..232df10 100644 --- a/include/cpphibernate/driver/mariadb/impl/read.h +++ b/include/cpphibernate/driver/mariadb/impl/read.h @@ -9,85 +9,228 @@ beg_namespace_cpphibernate_driver_mariadb { - /* read_impl_t */ - - template - struct read_impl_t + namespace __impl { - using dataset_type = T_dataset; - struct context_impl - : public read_context + template + struct make_read_context_impl { - mutable size_t count; + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); } + }; - template - context_impl(T_read_context&& p_read_context) - : read_context (std::forward(p_read_context)) - , count (0) - { } + template + struct make_read_context_impl< + mp::list, + mp::enable_if_c< + !misc::is_container>::value + && !misc::is_nullable>::value>> + { + using dataset_type = mp::decay_t; - 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 + context_impl(dataset_type& dataset, X_args&&... args) + : read_context (std::forward(args)...) + , _count (0) + , _dataset (dataset) + { + is_dynamic = false; + base_dataset_id = misc::get_type_id(hana::type_c); + } + + 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(args)...); } }; - static inline void apply(const read_context& context) + template + struct make_read_context_impl< + mp::list, + mp::enable_if_c< + !misc::is_container>::value + && misc::is_nullable>::value>> { - auto dataset_id = misc::get_type_id(hana::type_c); - 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 - struct read_impl_t< - T_dataset, - mp::enable_if>> - { - using dataset_type = T_dataset; - using nullable_helper_type = misc::nullable_helper; + using dataset_type = mp::decay_t; + using nullable_helper_type = misc::nullable_helper; + 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 + context_impl(dataset_type& dataset, X_args&&... args) + : read_context (std::forward(args)...) + , _dataset (dataset) + , _count (0) + { + is_dynamic = misc::is_pointer::value; + base_dataset_id = misc::get_type_id(hana::type_c); + nullable_helper_type::clear(_dataset); + } + + private: + virtual void* emplace_intern(void* data, size_t dataset_id) const override + { + if (data && !misc::is_pointer::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(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(args)...); } + }; + + template + struct make_read_context_impl< + mp::list, + mp::enable_if_c< + misc::is_container>::value + && !misc::is_nullable>::value>> { + using dataset_type = mp::decay_t; + using real_dataset_type = misc::real_dataset_t; + using container_helper_type = misc::container_helper; + 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 + context_impl(dataset_type& dataset, X_args&&... args) + : read_context (std::forward(args)...) + , _dataset (dataset) + , _count (0) + { + is_dynamic = misc::is_pointer::value; + base_dataset_id = misc::get_type_id(hana::type_c); + 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(args)...); } + }; - } - }; + } - /* read_impl_t - container */ + constexpr decltype(auto) make_read_context = misc::make_generic_predicate<__impl::make_read_context_impl> { }; - template - struct read_impl_t< - T_dataset, - mp::enable_if>> + namespace __impl { - using dataset_type = T_dataset; - static inline void apply(const read_context& context) + template + struct make_fake_context_impl + { + template + static constexpr decltype(auto) apply(T_args&&... args) + { static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fake_context(...)!"); } + }; + + template + struct make_fake_context_impl< + mp::list, + mp::enable_if_c< + hana::is_a_t::value>> { + using wrapped_dataset_type = mp::decay_t; + using dataset_type = misc::unwrap_t; + using real_dataset_type = misc::real_dataset_t; + + struct context_impl + : public read_context + { + public: + template + context_impl(X_args&&... args) + : read_context (std::forward(args)...) + { + is_dynamic = misc::is_pointer::value; + base_dataset_id = misc::get_type_id(hana::type_c); + } + + 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(args)...); } + }; + + } - } - }; + constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { }; } end_namespace_cpphibernate_driver_mariadb \ No newline at end of file diff --git a/include/cpphibernate/driver/mariadb/impl/where.h b/include/cpphibernate/driver/mariadb/impl/where.h index 9af2d15..1c81709 100644 --- a/include/cpphibernate/driver/mariadb/impl/where.h +++ b/include/cpphibernate/driver/mariadb/impl/where.h @@ -98,7 +98,6 @@ beg_namespace_cpphibernate_driver_mariadb }, []{ }); }); - os << ")"; statement.assign(os.str()); } }; diff --git a/include/cpphibernate/driver/mariadb/mariadb.h b/include/cpphibernate/driver/mariadb/mariadb.h index f84fd27..745a9a0 100644 --- a/include/cpphibernate/driver/mariadb/mariadb.h +++ b/include/cpphibernate/driver/mariadb/mariadb.h @@ -44,11 +44,19 @@ beg_namespace_cpphibernate_driver_mariadb template inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const { - read_context context(dataset, _schema, _connection, _filter); + using dataset_type = mp::decay_t; + using real_dataset_type = misc::real_dataset_t; + + auto dataset_id = misc::get_type_id(hana::type_c); + auto& table = _schema.table(dataset_id); + auto context = make_read_context(dataset, _schema, _connection, _filter); context.where = build_where(_schema, modifiers).query(_connection); context.limit = build_limit(modifiers).query(_connection); context.order_by = build_order_by(_schema, modifiers).query(_connection); - read_impl_t::apply(context); + + transaction_lock trans(_connection); + table.read(context); + trans.commit(); } }; diff --git a/include/cpphibernate/driver/mariadb/schema/field.fwd.h b/include/cpphibernate/driver/mariadb/schema/field.fwd.h index 505d088..275ffc8 100644 --- a/include/cpphibernate/driver/mariadb/schema/field.fwd.h +++ b/include/cpphibernate/driver/mariadb/schema/field.fwd.h @@ -6,7 +6,7 @@ beg_namespace_cpphibernate_driver_mariadb { - + /* field_t */ struct field_t; diff --git a/include/cpphibernate/driver/mariadb/schema/field.h b/include/cpphibernate/driver/mariadb/schema/field.h index 5365dde..f4a3fce 100644 --- a/include/cpphibernate/driver/mariadb/schema/field.h +++ b/include/cpphibernate/driver/mariadb/schema/field.h @@ -24,6 +24,7 @@ beg_namespace_cpphibernate_driver_mariadb 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_pointer { false }; // value is stored in a pointer 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_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) , real_value_id (std::move(other).real_value_id) , 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_auto_incremented (std::move(other).value_is_auto_incremented) , table (nullptr) @@ -77,7 +79,7 @@ beg_namespace_cpphibernate_driver_mariadb using read_context_ptr = std::unique_ptr; 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 */ virtual value_t get (const data_context& context) const; @@ -180,7 +182,7 @@ beg_namespace_cpphibernate_driver_mariadb public: 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; }; } diff --git a/include/cpphibernate/driver/mariadb/schema/field.inl b/include/cpphibernate/driver/mariadb/schema/field.inl index 0a9b083..e63adec 100644 --- a/include/cpphibernate/driver/mariadb/schema/field.inl +++ b/include/cpphibernate/driver/mariadb/schema/field.inl @@ -21,6 +21,7 @@ beg_namespace_cpphibernate_driver_mariadb real_value_id = misc::get_type_id(hana::type_c); value_is_nullable = misc::is_nullable::value; + value_is_pointer = misc::is_pointer::value; value_is_container = misc::is_container::value; value_is_ordered = misc::is_ordered::value; } @@ -39,7 +40,7 @@ beg_namespace_cpphibernate_driver_mariadb value_t value_field_t ::get(const data_context& context) const { - auto& dataset = context.get(this->table); + auto& dataset = context.get(); return type_props::convert_from(this->field.getter(dataset)); } @@ -47,7 +48,7 @@ beg_namespace_cpphibernate_driver_mariadb void value_field_t ::set(const data_context& context, const value_t& value) const { - auto& dataset = context.get(this->table); + auto& dataset = context.get(); this->field.setter(dataset, type_props::convert_to(value)); } @@ -89,38 +90,22 @@ beg_namespace_cpphibernate_driver_mariadb template read_context_ptr foreign_table_field_t - ::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; - return hana::eval_if( - is_nullable_type { }, - [&](auto _) { - auto& dataset = _(context).template get(); - auto& member = this->field.getter(dataset); - using nullable_helper_type = misc::nullable_helper>; - 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(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(); - auto& member = this->field.getter(dataset); - auto new_context = change_context(context, member); - return std::make_unique(new_context); - }); + if (!fake_context) + { + auto& dataset = context.get(); + 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; + return std::make_unique(new_context); + } + else + { + auto new_context = make_fake_context(hana::type_c, context.schema, context.connection, context.filter); + using context_type = mp::decay_t; + return std::make_unique(new_context); + } } namespace __impl diff --git a/include/cpphibernate/driver/mariadb/schema/table.fwd.h b/include/cpphibernate/driver/mariadb/schema/table.fwd.h index 67dea6e..d63f620 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.fwd.h +++ b/include/cpphibernate/driver/mariadb/schema/table.fwd.h @@ -6,7 +6,7 @@ beg_namespace_cpphibernate_driver_mariadb { - + /* table_t */ struct table_t; diff --git a/include/cpphibernate/driver/mariadb/schema/table.h b/include/cpphibernate/driver/mariadb/schema/table.h index ac5aa5d..cbf6402 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.h +++ b/include/cpphibernate/driver/mariadb/schema/table.h @@ -64,7 +64,10 @@ beg_namespace_cpphibernate_driver_mariadb 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 */ 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 base_dataset_type = typename base_type::base_dataset_type; using dataset_type = typename table_type::dataset_type; + using real_dataset_type = misc::real_dataset_t; using base_type::base_type; + virtual void emplace(const read_context& context) const override; + private: template constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const; diff --git a/include/cpphibernate/driver/mariadb/schema/table.inl b/include/cpphibernate/driver/mariadb/schema/table.inl index 812bb64..fe990d2 100644 --- a/include/cpphibernate/driver/mariadb/schema/table.inl +++ b/include/cpphibernate/driver/mariadb/schema/table.inl @@ -8,6 +8,11 @@ beg_namespace_cpphibernate_driver_mariadb /* table_polymorphic_t */ + template + void table_polymorphic_t + ::emplace(const read_context& context) const + { context.emplace(this); } + template template constexpr void table_polymorphic_t @@ -42,7 +47,7 @@ beg_namespace_cpphibernate_driver_mariadb { using derived_dataset_type = mp::decay_t; auto derived_dataset_id = misc::get_type_id(hana::type_c); - auto derived_table = this->get_derived(derived_dataset_id); + auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id); if (!derived_table) { throw misc::hibernate_exception(static_cast(std::ostringstream { } @@ -71,7 +76,7 @@ beg_namespace_cpphibernate_driver_mariadb }, [this, &context](auto _)->std::string { using tmp_type = misc::decay_unwrap_t))>; - assert(base_table); + assert(this->base_table); auto& dataset = context.get(); auto& base = static_cast(dataset); return this->base_table->create_update_exec(change_context(context, base)); diff --git a/include/cpphibernate/misc.h b/include/cpphibernate/misc.h index 2d226d2..04de4ef 100644 --- a/include/cpphibernate/misc.h +++ b/include/cpphibernate/misc.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include diff --git a/include/cpphibernate/misc/general.h b/include/cpphibernate/misc/general.h index 037db6c..181bfea 100644 --- a/include/cpphibernate/misc/general.h +++ b/include/cpphibernate/misc/general.h @@ -1,5 +1,7 @@ #pragma once +#include // TODO debug! + #include #include diff --git a/include/cpphibernate/misc/nullable_helper.h b/include/cpphibernate/misc/nullable_helper.h index d358b31..5ed9ea9 100644 --- a/include/cpphibernate/misc/nullable_helper.h +++ b/include/cpphibernate/misc/nullable_helper.h @@ -37,6 +37,9 @@ beg_namespace_cpphibernate_misc static inline const value_type* get(const nullable_type& x) { 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) { return *(x = value); } @@ -58,9 +61,9 @@ beg_namespace_cpphibernate_misc static inline value_type* get(const nullable_type& x) { 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; } @@ -70,6 +73,12 @@ beg_namespace_cpphibernate_misc 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) { return x.reset(); } }; @@ -85,9 +94,9 @@ beg_namespace_cpphibernate_misc static inline value_type* get(const nullable_type& x) { 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; } @@ -97,6 +106,12 @@ beg_namespace_cpphibernate_misc 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) { return x.reset(); } }; diff --git a/src/driver/mariadb/schema/field.cpp b/src/driver/mariadb/schema/field.cpp index 091571b..986e522 100644 --- a/src/driver/mariadb/schema/field.cpp +++ b/src/driver/mariadb/schema/field.cpp @@ -24,6 +24,7 @@ void field_t::print(std::ostream& os) const << indent << "\"value_id\": " << value_id << "," << indent << "\"real_value_id\": " << real_value_id << "," << 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_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << "," << indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," @@ -150,7 +151,7 @@ void field_t::update() /* CRUD */ 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 */ diff --git a/src/driver/mariadb/schema/table.cpp b/src/driver/mariadb/schema/table.cpp index f4b2b60..de787cc 100644 --- a/src/driver/mariadb/schema/table.cpp +++ b/src/driver/mariadb/schema/table.cpp @@ -15,11 +15,23 @@ using namespace ::cpphibernate::driver::mariadb_impl; /* 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 + { }; + struct data_extractor_t { const table_t& _table; const read_context& _context; const ::cppmariadb::row& _row; + foreign_many_list_t& _foreign_many_list; const filter_t& _filter; mutable size_t _index; @@ -27,14 +39,19 @@ struct data_extractor_t data_extractor_t( const table_t& p_table, 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 { value_t ret; @@ -44,26 +61,67 @@ struct data_extractor_t 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; + 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(value) && !skip) + { + auto type = utl::from_string(*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)) - return; + return skip; /* primary key */ assert(table.primary_key_field); - read_field(*table.primary_key_field, context); + read_field(*table.primary_key_field, context, skip); /* data fields */ for (auto& ptr : table.data_fields) @@ -71,7 +129,7 @@ struct data_extractor_t assert(ptr); auto& field = *ptr; if (!_context.filter.is_excluded(field)) - read_field(field, context); + read_field(field, context, skip); } /* foreign table one */ @@ -87,19 +145,54 @@ struct data_extractor_t || _filter.is_excluded(ref_table)) 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(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 { _index = 0; - _context.emplace(); - read_table(_table, &_context); + read_table(_table, _context, true, true, false); if (_index != _row.size()) throw misc::hibernate_exception("result was not completely read!"); } @@ -109,14 +202,23 @@ struct data_extractor_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 joins; select_query_builder_t( const table_t& p_table, @@ -127,7 +229,7 @@ struct select_query_builder_t , _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++); } 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; } - 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; - 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 */ - 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 << ", "; os << "`" - << table.table_name + << ctx.table.table_name << "`.`__type` AS `__type`"; ret = true; } - if (_filter.is_excluded(table)) + if (_filter.is_excluded(ctx.table)) return ret; ret = true; /* 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 */ - for (auto& ptr : table.data_fields) + for (auto& ptr : ctx.table.data_fields) { assert(ptr); auto& field = *ptr; if (!_filter.is_excluded(field)) - add_field(field, alias); + add_field(field, real_alias); } /* foreign table one */ - for (auto& ptr : table.foreign_table_one_fields) + for (auto& ptr : ctx.table.foreign_table_one_fields) { assert(ptr); assert(ptr->referenced_table); @@ -217,23 +338,84 @@ struct select_query_builder_t || _filter.is_excluded(ref_table)) 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; @@ -242,15 +424,21 @@ struct select_query_builder_t inline std::string operator()() { os << "SELECT "; - auto alias = make_alias(_table); - add_table(_table, alias); + add_table({ + _table, + "", + true, + true, + _is_dynamic, + }); os << " FROM `" << _table.table_name - << "` AS `" - << alias - << "`" - << join.str() - << " ?where! ?order! ?limit!"; + << "`"; + + for (auto& join : joins) + os << join; + + os << " ?where! ?order! ?limit!"; return os.str(); } }; @@ -958,19 +1146,35 @@ void table_t::print(std::ostream& os) const << 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) return this; for (auto ptr : derived_tables) { assert(ptr); - auto ret = ptr->get_derived(id); + auto ret = ptr->get_derived_by_dataset_id(id); if (ret) return ret; } 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 { 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& 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)); auto result = connection.execute_used(statement); if (!result) throw misc::hibernate_exception("Unable to fetching data from database!"); ::cppmariadb::row* row; + foreign_many_list_t foreign_many_list; while ((row = result->next())) - data_extractor_t(*this, context, *row)(); + data_extractor_t(*this, context, *row, foreign_many_list)(); 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); + } } \ No newline at end of file diff --git a/test/cpphibernate_read.cpp b/test/cpphibernate_read.cpp index 8d4ea76..a80cc1d 100644 --- a/test/cpphibernate_read.cpp +++ b/test/cpphibernate_read.cpp @@ -15,14 +15,16 @@ TEST(CppHibernateTests, read_test1) expect_query(mock, "START TRANSACTION"); 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 " - "`tbl_test1` AS `T0` ", + "`tbl_test1` " + "WHERE " + "(`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')) ", result_used({ { "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, "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 " - "`tbl_test2` AS `T0` ", + "`tbl_test2` " + "WHERE " + "(`tbl_test2`.`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')) ", result_used({ { "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, "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 " - "`tbl_test3` AS `T0` ", + "`tbl_test3` " + "WHERE " + "(`tbl_test3`.`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')) ", result_used({ { "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, "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 " - "`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 " - "`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({ { "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, "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`), " "`T2`.`u8_data`, " "`T2`.`i8_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 " - "`tbl_derived2` AS `T0` " + "`tbl_derived2` " + "JOIN " + "`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` " "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 " - "`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 " - "`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({ { "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_FALSE(static_cast(d2.test2_ptr_s)); } + +TEST(CppHibernateTests, read_derived3_static) +{ + StrictMock 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(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(d3.test2_nullable)); + EXPECT_FALSE(static_cast(d3.test2_ptr_u)); + EXPECT_FALSE(static_cast(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 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(0x1111), _, _, _)) + .Times(AnyNumber()) + .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); + + EXPECT_CALL( + mock, + mysql_close( + reinterpret_cast(0x1111))); + + ::cppmariadb::connection connection(reinterpret_cast(0x1111)); + auto context = make_context(test_schema, connection); + + constexpr decltype(auto) d2_key_field = test_schema.tables[5_c].fields[0_c]; + + std::unique_ptr d2_ptr; + context.read(d2_ptr, where(equal(d2_key_field, "3d1287a2-abb9-11e8-98d0-529269fb1459"))); + auto* d3_ptr = dynamic_cast(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(d3.test2_nullable)); + EXPECT_FALSE(static_cast(d3.test2_ptr_u)); + EXPECT_FALSE(static_cast(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()); + } +}