* implemented simple read methods of mariadb drivermaster
| @@ -73,18 +73,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| public: | public: | ||||
| template<typename T> | template<typename T> | ||||
| inline decltype(auto) get() const | |||||
| { | |||||
| if (!data) | |||||
| 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) | |||||
| { | |||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||||
| << "invalid type! expected " << data_id << ", got " << type_id).str()); | |||||
| } | |||||
| return *static_cast<T*>(data); | |||||
| } | |||||
| inline decltype(auto) get(const table_t* table = nullptr) const; | |||||
| template<typename T_data> | template<typename T_data> | ||||
| inline data_context( | inline data_context( | ||||
| @@ -141,5 +130,46 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| { } | { } | ||||
| }; | }; | ||||
| /* read_context */ | |||||
| struct read_context | |||||
| : public filter_context | |||||
| { | |||||
| bool is_dynamic; | |||||
| size_t base_dataset_id; | |||||
| std::string where; | |||||
| std::string limit; | |||||
| std::string order_by; | |||||
| template<typename T_data> | |||||
| 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) | |||||
| , is_dynamic (false) | |||||
| , base_dataset_id (0) | |||||
| { } | |||||
| virtual ~read_context() = default; | |||||
| template<typename T_dataset> | |||||
| inline T_dataset& emplace(const table_t* table = nullptr) const; | |||||
| void emplace() const | |||||
| { emplace_intern(nullptr); } | |||||
| 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!"); } | |||||
| }; | |||||
| } | } | ||||
| end_namespace_cpphibernate_driver_mariadb | end_namespace_cpphibernate_driver_mariadb | ||||
| @@ -0,0 +1,67 @@ | |||||
| #pragma once | |||||
| #include <cpphibernate/driver/mariadb/helper/context.h> | |||||
| beg_namespace_cpphibernate_driver_mariadb | |||||
| { | |||||
| /* data_context */ | |||||
| template<typename T> | |||||
| inline decltype(auto) data_context | |||||
| ::get(const table_t* table) const | |||||
| { | |||||
| if (!data) | |||||
| 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) | |||||
| { | |||||
| if (!table) | |||||
| table = &schema.table(type_id); | |||||
| while(table && table->dataset_id != type_id) | |||||
| table = table->base_table; | |||||
| if (!table) | |||||
| { | |||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | |||||
| << "invalid type! expected " << data_id << ", got " << type_id).str()); | |||||
| } | |||||
| } | |||||
| return *static_cast<T*>(data); | |||||
| } | |||||
| /* read_context */ | |||||
| template<typename T_dataset> | |||||
| 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!"); | |||||
| // check table | |||||
| auto dataset_id = misc::get_type_id(hana::type_c<mp::decay_t<T_dataset>>); | |||||
| 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!"); | |||||
| // 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<T_dataset>::name() + " is not a derived type of table " + table->table_name); | |||||
| // 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); | |||||
| } | |||||
| } | |||||
| end_namespace_cpphibernate_driver_mariadb | |||||
| @@ -2,4 +2,8 @@ | |||||
| #include <cpphibernate/driver/mariadb/impl/create_update.h> | #include <cpphibernate/driver/mariadb/impl/create_update.h> | ||||
| #include <cpphibernate/driver/mariadb/impl/limit.h> | #include <cpphibernate/driver/mariadb/impl/limit.h> | ||||
| #include <cpphibernate/driver/mariadb/impl/where.h> | |||||
| #include <cpphibernate/driver/mariadb/impl/order_by.h> | |||||
| #include <cpphibernate/driver/mariadb/impl/read.h> | |||||
| #include <cpphibernate/driver/mariadb/impl/where.h> | |||||
| #include <cpphibernate/driver/mariadb/helper/context.inl> | |||||
| @@ -22,17 +22,17 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| hana::for_each(modifier, [&limit, &offset](auto& modifier){ | hana::for_each(modifier, [&limit, &offset](auto& modifier){ | ||||
| using modifier_type = mp::decay_t<decltype(modifier)>; | using modifier_type = mp::decay_t<decltype(modifier)>; | ||||
| using is_limit_type = modifier::is_limit_modifier<modifier_type>; | using is_limit_type = modifier::is_limit_modifier<modifier_type>; | ||||
| using is_offset_type = modifier::is_offset_modifier<modifier_type>; | |||||
| using is_offset_type = modifier::is_offset<modifier_type>; | |||||
| hana::eval_if( | hana::eval_if( | ||||
| is_limit_type { }, | is_limit_type { }, | ||||
| [&limit, &modifier](auto _){ | [&limit, &modifier](auto _){ | ||||
| limit = hana::value(_(modifier).value); | |||||
| limit = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||||
| }, | }, | ||||
| [&offset, &modifier](){ | [&offset, &modifier](){ | ||||
| hana::eval_if( | hana::eval_if( | ||||
| is_offset_type { }, | is_offset_type { }, | ||||
| [&offset, &modifier](auto _){ | [&offset, &modifier](auto _){ | ||||
| offset = hana::value(_(modifier).value); | |||||
| offset = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||||
| }, | }, | ||||
| []{ | []{ | ||||
| /* no-op */ | /* no-op */ | ||||
| @@ -48,7 +48,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| std::ostringstream ss; | std::ostringstream ss; | ||||
| ss << "LIMIT " << limit; | ss << "LIMIT " << limit; | ||||
| if (offset >= 0) | if (offset >= 0) | ||||
| ss << " OFFSET" << offset; | |||||
| ss << " OFFSET " << offset; | |||||
| statement.assign(ss.str()); | statement.assign(ss.str()); | ||||
| } | } | ||||
| } | } | ||||
| @@ -24,7 +24,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| mp::list<T_modifier>, | mp::list<T_modifier>, | ||||
| mp::enable_if_c< | mp::enable_if_c< | ||||
| modifier::is_limit_modifier<mp::decay_t<T_modifier>>::value | modifier::is_limit_modifier<mp::decay_t<T_modifier>>::value | ||||
| || modifier::is_offset_modifier<mp::decay_t<T_modifier>>::value>> | |||||
| || modifier::is_offset<mp::decay_t<T_modifier>>::value>> | |||||
| { | { | ||||
| static constexpr decltype(auto) apply(T_modifier&&) | static constexpr decltype(auto) apply(T_modifier&&) | ||||
| { return T_modifier { }; } | { return T_modifier { }; } | ||||
| @@ -0,0 +1,83 @@ | |||||
| #pragma once | |||||
| #include <sstream> | |||||
| #include <cpphibernate/config.h> | |||||
| #include <cpphibernate/modifier.h> | |||||
| #include <cpphibernate/driver/mariadb/schema/schema.h> | |||||
| beg_namespace_cpphibernate_driver_mariadb | |||||
| { | |||||
| /* order_by_builder */ | |||||
| template<typename T_modifiers> | |||||
| struct order_by_builder | |||||
| { | |||||
| private: | |||||
| struct build_t | |||||
| { | |||||
| const schema_t& schema; | |||||
| const T_modifiers& modifiers; | |||||
| std::ostringstream os; | |||||
| inline build_t(const schema_t& p_schema, const T_modifiers& p_modifiers) | |||||
| : schema (p_schema) | |||||
| , modifiers (p_modifiers) | |||||
| { } | |||||
| inline void build(::cppmariadb::statement& statement) | |||||
| { | |||||
| size_t index = 0; | |||||
| hana::for_each(modifiers, [&](auto& modifier){ | |||||
| using modifier_type = mp::decay_t<decltype(modifier)>; | |||||
| using is_order_by_type = modifier::is_order_by<modifier_type>; | |||||
| hana::eval_if( | |||||
| is_order_by_type { }, | |||||
| [&](auto _){ | |||||
| hana::for_each(_(modifier).fields, [&](auto& f){ | |||||
| using field_type = mp::decay_t<decltype(f)>; | |||||
| using is_ascencing_type = modifier::is_ascending<field_type>; | |||||
| if (index++ == 0) os << "ORDER_BY "; | |||||
| else os << ", "; | |||||
| auto& field = schema.field(misc::get_type_id(f.wrapped_field)); | |||||
| os << "`" | |||||
| << field.table_name | |||||
| << "`.`" | |||||
| << field.field_name | |||||
| << "` " | |||||
| << (is_ascencing_type::value | |||||
| ? "ASC" | |||||
| : "DESC"); | |||||
| }); | |||||
| }, | |||||
| []{ }); | |||||
| }); | |||||
| statement.assign(os.str()); | |||||
| } | |||||
| }; | |||||
| private: | |||||
| const schema_t* _schema { nullptr }; | |||||
| ::cppmariadb::statement _statement; | |||||
| public: | |||||
| inline ::cppmariadb::statement& assign(const schema_t& schema, const T_modifiers& modifiers) | |||||
| { | |||||
| if (_schema != &schema) | |||||
| { | |||||
| build_t(schema, modifiers).build(_statement); | |||||
| _schema = &schema; | |||||
| } | |||||
| return _statement; | |||||
| } | |||||
| }; | |||||
| template<typename T_modifiers> | |||||
| inline decltype(auto) build_order_by(const schema_t& schema, const T_modifiers& modifiers) | |||||
| { | |||||
| static order_by_builder<T_modifiers> builder; | |||||
| return builder.assign(schema, modifiers); | |||||
| } | |||||
| } | |||||
| end_namespace_cpphibernate_driver_mariadb | |||||
| @@ -0,0 +1,93 @@ | |||||
| #pragma once | |||||
| #include <cppmariadb.h> | |||||
| #include <cpphibernate/misc.h> | |||||
| #include <cpphibernate/config.h> | |||||
| #include <cpphibernate/driver/mariadb/helper.h> | |||||
| #include <cpphibernate/driver/mariadb/schema/schema.h> | |||||
| beg_namespace_cpphibernate_driver_mariadb | |||||
| { | |||||
| /* read_impl_t */ | |||||
| template<typename T_dataset, typename = void> | |||||
| struct read_impl_t | |||||
| { | |||||
| using dataset_type = T_dataset; | |||||
| struct context_impl | |||||
| : public read_context | |||||
| { | |||||
| mutable size_t count; | |||||
| 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) | |||||
| { } | |||||
| private: | |||||
| virtual void* emplace_intern(void* data) const override | |||||
| { | |||||
| 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!"); | |||||
| } | |||||
| }; | |||||
| static inline void apply(const read_context& context) | |||||
| { | |||||
| 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>; | |||||
| static inline void apply(const read_context& context) | |||||
| { | |||||
| } | |||||
| }; | |||||
| /* read_impl_t - container */ | |||||
| template<typename T_dataset> | |||||
| struct read_impl_t< | |||||
| T_dataset, | |||||
| mp::enable_if<misc::is_container<T_dataset>>> | |||||
| { | |||||
| using dataset_type = T_dataset; | |||||
| static inline void apply(const read_context& context) | |||||
| { | |||||
| } | |||||
| }; | |||||
| } | |||||
| end_namespace_cpphibernate_driver_mariadb | |||||
| @@ -87,7 +87,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| size_t index = 0; | size_t index = 0; | ||||
| hana::for_each(modifiers, [&](auto& modifier){ | hana::for_each(modifiers, [&](auto& modifier){ | ||||
| using modifier_type = mp::decay_t<decltype(modifier)>; | using modifier_type = mp::decay_t<decltype(modifier)>; | ||||
| using is_where_type = modifier::is_where_modifier<modifier_type>; | |||||
| using is_where_type = modifier::is_where<modifier_type>; | |||||
| hana::eval_if( | hana::eval_if( | ||||
| is_where_type { }, | is_where_type { }, | ||||
| [&](auto _){ | [&](auto _){ | ||||
| @@ -144,7 +144,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| { | { | ||||
| hana::for_each(modifiers, [&](auto& modifier){ | hana::for_each(modifiers, [&](auto& modifier){ | ||||
| using modifier_type = mp::decay_t<decltype(modifier)>; | using modifier_type = mp::decay_t<decltype(modifier)>; | ||||
| using is_where_type = modifier::is_where_modifier<modifier_type>; | |||||
| using is_where_type = modifier::is_where<modifier_type>; | |||||
| hana::eval_if( | hana::eval_if( | ||||
| is_where_type { }, | is_where_type { }, | ||||
| [&](auto _){ | [&](auto _){ | ||||
| @@ -44,10 +44,11 @@ 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 | ||||
| { | { | ||||
| auto& where = build_where(_schema, modifiers); | |||||
| auto& limit = build_limit(modifiers); | |||||
| std::cout << "WHERE = " << where.query(_connection) << std::endl; | |||||
| std::cout << "LIMIT = " << limit.query(_connection) << std::endl; | |||||
| read_context 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<T_dataset>::apply(context); | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -39,7 +39,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>(); | |||||
| auto& dataset = context.get<dataset_type>(this->table); | |||||
| return type_props::convert_from(this->field.getter(dataset)); | return type_props::convert_from(this->field.getter(dataset)); | ||||
| } | } | ||||
| @@ -47,7 +47,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>(); | |||||
| auto& dataset = context.get<dataset_type>(this->table); | |||||
| this->field.setter(dataset, type_props::convert_to(value)); | this->field.setter(dataset, type_props::convert_to(value)); | ||||
| } | } | ||||
| @@ -16,12 +16,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| using field_set_type = std::set<const field_t*>; | using field_set_type = std::set<const field_t*>; | ||||
| using table_set_type = std::set<const table_t*>; | using table_set_type = std::set<const table_t*>; | ||||
| size_t cache_id; | |||||
| size_t cache_id { 0 }; | |||||
| field_set_type fields; | field_set_type fields; | ||||
| table_set_type tables; | table_set_type tables; | ||||
| bool contains(const table_t* table, bool check_base) const; | |||||
| bool contains(const field_t* field) const; | |||||
| inline bool is_excluded(const table_t& table) const | |||||
| { return tables.count(&table); } | |||||
| inline bool is_excluded(const field_t& field) const | |||||
| { return fields.count(&field); } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -76,8 +76,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| inline decltype(auto) create_update(const create_update_context& context) const | inline decltype(auto) create_update(const create_update_context& context) const | ||||
| { return create_update_intern(context); } | { return create_update_intern(context); } | ||||
| inline void read() const | |||||
| { } | |||||
| inline void read(const read_context& context) const | |||||
| { return read_exec(context); } | |||||
| private: | private: | ||||
| template<typename T_schema, typename T_table, typename T_base_dataset> | template<typename T_schema, typename T_table, typename T_base_dataset> | ||||
| @@ -87,14 +87,18 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| friend struct table_polymorphic_t; | friend struct table_polymorphic_t; | ||||
| using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | ||||
| using statement_map = std::map<size_t, ::cppmariadb::statement>; | |||||
| mutable statement_ptr _statement_create_table; | mutable statement_ptr _statement_create_table; | ||||
| mutable statement_ptr _statement_alter_table; | mutable statement_ptr _statement_alter_table; | ||||
| mutable statement_ptr _statement_insert_into; | mutable statement_ptr _statement_insert_into; | ||||
| mutable statement_map _statement_select_static; | |||||
| mutable statement_map _statement_select_dynamic; | |||||
| ::cppmariadb::statement& get_statement_create_table() const; | ::cppmariadb::statement& get_statement_create_table() const; | ||||
| ::cppmariadb::statement* get_statement_alter_table() const; | ::cppmariadb::statement* get_statement_alter_table() const; | ||||
| ::cppmariadb::statement& get_statement_insert_into() const; | ::cppmariadb::statement& get_statement_insert_into() const; | ||||
| ::cppmariadb::statement& get_statement_select(const read_context& context) const; | |||||
| std::string execute_create_update( | std::string execute_create_update( | ||||
| const create_update_context& context, | const create_update_context& context, | ||||
| @@ -109,6 +113,8 @@ beg_namespace_cpphibernate_driver_mariadb | |||||
| virtual std::string create_update_intern(const create_update_context& context) const; | virtual std::string create_update_intern(const create_update_context& context) const; | ||||
| std::string create_update_exec (const create_update_context& context) const; | std::string create_update_exec (const create_update_context& context) const; | ||||
| void read_exec (const read_context& context) const; | |||||
| }; | }; | ||||
| /* table_simple_t */ | /* table_simple_t */ | ||||
| @@ -12,10 +12,40 @@ beg_namespace_cpphibernate_misc | |||||
| /* container_helper */ | /* container_helper */ | ||||
| template<typename T_container, typename = void> | template<typename T_container, typename = void> | ||||
| struct container_helper | |||||
| struct container_helper; | |||||
| template<typename T_value, typename... T_args> | |||||
| struct container_helper<std::vector<T_value, T_args...>, void> | |||||
| { | |||||
| using container_type = std::vector<T_value, T_args...>; | |||||
| using value_type = T_value; | |||||
| template<typename... T_xargs> | |||||
| static inline value_type& emplace(container_type& container, T_xargs&&... args) | |||||
| { | |||||
| container.emplace_back(std::forward<T_xargs>(args)...); | |||||
| return container.back(); | |||||
| } | |||||
| static inline void clear(container_type& container) | |||||
| { container.clear(); } | |||||
| }; | |||||
| template<typename T_value, typename... T_args> | |||||
| struct container_helper<std::list<T_value, T_args...>, void> | |||||
| { | { | ||||
| using container_type = T_container; | |||||
| using value_type = real_dataset_t<container_type>; | |||||
| using container_type = std::list<T_value, T_args...>; | |||||
| using value_type = T_value; | |||||
| template<typename... T_xargs> | |||||
| static inline value_type& emplace(container_type& container, T_xargs&&... args) | |||||
| { | |||||
| container.emplace_back(std::forward<T_xargs>(args)...); | |||||
| return container.back(); | |||||
| } | |||||
| static inline void clear(container_type& container) | |||||
| { container.clear(); } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -4,4 +4,5 @@ | |||||
| #include <cpphibernate/modifier/modifier.h> | #include <cpphibernate/modifier/modifier.h> | ||||
| #include <cpphibernate/modifier/modifiers.h> | #include <cpphibernate/modifier/modifiers.h> | ||||
| #include <cpphibernate/modifier/offset.h> | #include <cpphibernate/modifier/offset.h> | ||||
| #include <cpphibernate/modifier/order_by.h> | |||||
| #include <cpphibernate/modifier/where.h> | #include <cpphibernate/modifier/where.h> | ||||
| @@ -19,32 +19,6 @@ beg_namespace_cpphibernate_modifier | |||||
| using value_type = T_value; | using value_type = T_value; | ||||
| value_type value; | value_type value; | ||||
| constexpr limit_t(T_value&& p_value) | |||||
| : value(std::forward<T_value>(p_value)) | |||||
| { } | |||||
| }; | |||||
| /* limit_builder */ | |||||
| template<typename X, typename = void> | |||||
| struct limit_builder | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::limit(...)!"); } | |||||
| }; | |||||
| template<typename T_value> | |||||
| struct limit_builder< | |||||
| mp::list<T_value>, | |||||
| mp::enable_if<mp::is_integral<T_value>>> | |||||
| { | |||||
| static constexpr decltype(auto) apply(T_value&& value) | |||||
| { | |||||
| using value_type = mp::integral_constant<T_value, value>; | |||||
| return limit_t<value_type>(value_type { }); | |||||
| } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -58,7 +32,8 @@ beg_namespace_cpphibernate_modifier | |||||
| /* make */ | /* make */ | ||||
| constexpr decltype(auto) limit = misc::make_generic_predicate<__impl::limit_builder> { }; | |||||
| template<size_t T_value> | |||||
| constexpr decltype(auto) limit = __impl::limit_t<hana::size_t<T_value>> { }; | |||||
| } | } | ||||
| end_namespace_cpphibernate_modifier | end_namespace_cpphibernate_modifier | ||||
| @@ -19,32 +19,6 @@ beg_namespace_cpphibernate_modifier | |||||
| using value_type = T_value; | using value_type = T_value; | ||||
| value_type value; | value_type value; | ||||
| constexpr offset_t(T_value&& p_value) | |||||
| : value(std::forward<T_value>(p_value)) | |||||
| { } | |||||
| }; | |||||
| /* offset_builder */ | |||||
| template<typename X, typename = void> | |||||
| struct offset_builder | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::offset(...)!"); } | |||||
| }; | |||||
| template<typename T_value> | |||||
| struct offset_builder< | |||||
| mp::list<T_value>, | |||||
| mp::enable_if<mp::is_integral<T_value>>> | |||||
| { | |||||
| static constexpr decltype(auto) apply(T_value&& value) | |||||
| { | |||||
| using value_type = mp::integral_constant<T_value, value>; | |||||
| return offset_t<value_type>(value_type { }); | |||||
| } | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -52,13 +26,14 @@ beg_namespace_cpphibernate_modifier | |||||
| /* meta */ | /* meta */ | ||||
| template<typename T> | template<typename T> | ||||
| struct is_offset_modifier | |||||
| struct is_offset | |||||
| : public mp::is_specialization_of<T, __impl::offset_t> | : public mp::is_specialization_of<T, __impl::offset_t> | ||||
| { }; | { }; | ||||
| /* make */ | /* make */ | ||||
| constexpr decltype(auto) offset = misc::make_generic_predicate<__impl::offset_builder> { }; | |||||
| template<size_t T_value> | |||||
| constexpr decltype(auto) offset = __impl::offset_t<hana::size_t<T_value>> { }; | |||||
| } | } | ||||
| end_namespace_cpphibernate_modifier | end_namespace_cpphibernate_modifier | ||||
| @@ -0,0 +1,171 @@ | |||||
| #pragma once | |||||
| #include <cpphibernate/misc.h> | |||||
| #include <cpphibernate/config.h> | |||||
| #include <cpphibernate/modifier/modifier.h> | |||||
| beg_namespace_cpphibernate_modifier | |||||
| { | |||||
| namespace __impl | |||||
| { | |||||
| /* order_direction_tag_t */ | |||||
| struct order_direction_tag_t | |||||
| { }; | |||||
| /* order_direction_t */ | |||||
| template<typename T_field> | |||||
| struct order_direction_t | |||||
| : public order_direction_tag_t | |||||
| { | |||||
| using field_type = T_field; | |||||
| using wrapped_field_type = hana::type<mp::decay_t<field_type>>; | |||||
| wrapped_field_type wrapped_field; | |||||
| }; | |||||
| } | |||||
| /* meta */ | |||||
| template<typename T> | |||||
| struct is_order_direction | |||||
| : public mp::is_base_of<__impl::order_direction_tag_t, T> | |||||
| { }; | |||||
| template<typename... T> | |||||
| struct all_are_order_directions | |||||
| : public mp::all_true<is_order_direction<T>::value...> | |||||
| { }; | |||||
| namespace __impl | |||||
| { | |||||
| /* order_ascending_t */ | |||||
| template<typename T_field> | |||||
| struct order_ascending_t | |||||
| : public order_direction_t<T_field> | |||||
| { }; | |||||
| /* order_descending_t */ | |||||
| template<typename T_field> | |||||
| struct order_descending_t | |||||
| : public order_direction_t<T_field> | |||||
| { }; | |||||
| /* order_by_t */ | |||||
| template<typename... T_fields> | |||||
| struct order_by_t | |||||
| : public modifier_t | |||||
| { | |||||
| using fields_type = hana::basic_tuple<T_fields...>; | |||||
| fields_type fields; | |||||
| }; | |||||
| } | |||||
| /* meta */ | |||||
| template<typename T> | |||||
| struct is_order_by | |||||
| : public mp::is_specialization_of<T, __impl::order_by_t> | |||||
| { }; | |||||
| template<typename T> | |||||
| struct is_ascending | |||||
| : public mp::is_specialization_of<T, __impl::order_ascending_t> | |||||
| { }; | |||||
| template<typename T> | |||||
| struct is_descending | |||||
| : public mp::is_specialization_of<T, __impl::order_descending_t> | |||||
| { }; | |||||
| namespace __impl | |||||
| { | |||||
| /* ascending_builder */ | |||||
| template<typename X, typename = void> | |||||
| struct ascending_builder | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::ascending(...)!"); } | |||||
| }; | |||||
| template<typename T_field> | |||||
| struct ascending_builder< | |||||
| mp::list<T_field>, | |||||
| mp::enable_if<schema::is_field<mp::decay_t<T_field>>>> | |||||
| { | |||||
| static constexpr decltype(auto) apply(T_field&&) | |||||
| { | |||||
| using field_type = mp::decay_t<T_field>; | |||||
| using ascending_type = order_ascending_t<field_type>; | |||||
| return ascending_type { }; | |||||
| } | |||||
| }; | |||||
| /* descending_builder */ | |||||
| template<typename X, typename = void> | |||||
| struct descending_builder | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::descending(...)!"); } | |||||
| }; | |||||
| template<typename T_field> | |||||
| struct descending_builder< | |||||
| mp::list<T_field>, | |||||
| mp::enable_if<schema::is_field<mp::decay_t<T_field>>>> | |||||
| { | |||||
| static constexpr decltype(auto) apply(T_field&&) | |||||
| { | |||||
| using field_type = mp::decay_t<T_field>; | |||||
| using descending_type = order_descending_t<field_type>; | |||||
| return descending_type { }; | |||||
| } | |||||
| }; | |||||
| /* order_by_builder */ | |||||
| template<typename X, typename = void> | |||||
| struct order_by_builder | |||||
| { | |||||
| template<typename... T_args> | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::order_by(...)!"); } | |||||
| }; | |||||
| template<typename... T_order_directions> | |||||
| struct order_by_builder< | |||||
| mp::list<T_order_directions...>, | |||||
| mp::enable_if<all_are_order_directions<mp::decay_t<T_order_directions>...>>> | |||||
| { | |||||
| static constexpr decltype(auto) apply(T_order_directions&&...) | |||||
| { | |||||
| using order_by_type = order_by_t<mp::decay_t<T_order_directions>...>; | |||||
| return order_by_type { }; | |||||
| } | |||||
| }; | |||||
| } | |||||
| /* make */ | |||||
| constexpr decltype(auto) ascending = misc::make_generic_predicate<__impl::ascending_builder> { }; | |||||
| constexpr decltype(auto) descending = misc::make_generic_predicate<__impl::descending_builder> { }; | |||||
| constexpr decltype(auto) order_by = misc::make_generic_predicate<__impl::order_by_builder> { }; | |||||
| } | |||||
| end_namespace_cpphibernate_modifier | |||||
| @@ -32,7 +32,7 @@ beg_namespace_cpphibernate_modifier | |||||
| struct where_builder | struct where_builder | ||||
| { | { | ||||
| template<typename... T_args> | template<typename... T_args> | ||||
| static constexpr decltype(auto) apply(T_args&... args) | |||||
| static constexpr decltype(auto) apply(T_args&&... args) | |||||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::where(...)!"); } | { static_assert(sizeof...(args) == -1, "Invalid parameters for hibernate::modifier::where(...)!"); } | ||||
| }; | }; | ||||
| @@ -48,7 +48,7 @@ beg_namespace_cpphibernate_modifier | |||||
| /* meta */ | /* meta */ | ||||
| template<typename T> | template<typename T> | ||||
| struct is_where_modifier | |||||
| struct is_where | |||||
| : public mp::is_specialization_of<T, __impl::where_t> | : public mp::is_specialization_of<T, __impl::where_t> | ||||
| { }; | { }; | ||||
| @@ -1,19 +0,0 @@ | |||||
| #include <cpphibernate/driver/mariadb/schema/table.h> | |||||
| #include <cpphibernate/driver/mariadb/schema/filter.h> | |||||
| using namespace ::cpphibernate::driver::mariadb_impl; | |||||
| bool filter_t::contains(const table_t* table, bool check_base) const | |||||
| { | |||||
| if (tables.count(table)) | |||||
| return true; | |||||
| else if (check_base && table->base_table) | |||||
| return contains(table->base_table, true); | |||||
| else | |||||
| return false; | |||||
| } | |||||
| bool filter_t::contains(const field_t* field) const | |||||
| { | |||||
| return (fields.count(field) > 0); | |||||
| } | |||||
| @@ -12,6 +12,173 @@ | |||||
| using namespace ::utl; | using namespace ::utl; | ||||
| using namespace ::cpphibernate::driver::mariadb_impl; | using namespace ::cpphibernate::driver::mariadb_impl; | ||||
| /* data_extractor_t */ | |||||
| struct data_extractor_t | |||||
| { | |||||
| const table_t& _table; | |||||
| const read_context& _context; | |||||
| const ::cppmariadb::row& _row; | |||||
| mutable size_t _index; | |||||
| 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) | |||||
| { } | |||||
| inline value_t get_value() const | |||||
| { | |||||
| value_t ret; | |||||
| auto f = _row.at(_index); | |||||
| if (!f.is_null()) | |||||
| ret = f.get<std::string>(); | |||||
| return ret; | |||||
| } | |||||
| inline void read_field(const field_t& field) const | |||||
| { | |||||
| field.set(_context, get_value()); | |||||
| ++_index; | |||||
| } | |||||
| inline void read_table(const table_t& table) const | |||||
| { | |||||
| if (table.base_table) | |||||
| read_table(*table.base_table); | |||||
| if (_context.filter.is_excluded(table)) | |||||
| return; | |||||
| /* primary key */ | |||||
| assert(table.primary_key_field); | |||||
| read_field(*table.primary_key_field); | |||||
| /* data fields */ | |||||
| for (auto& ptr : table.data_fields) | |||||
| { | |||||
| assert(ptr); | |||||
| auto& field = *ptr; | |||||
| if (!_context.filter.is_excluded(field)) | |||||
| read_field(field); | |||||
| } | |||||
| } | |||||
| inline void operator()() const | |||||
| { | |||||
| _index = 0; | |||||
| _context.emplace(); | |||||
| read_table(_table); | |||||
| } | |||||
| }; | |||||
| /* select_query_builder_t */ | |||||
| struct select_query_builder_t | |||||
| { | |||||
| const table_t& _table; | |||||
| const filter_t& _filter; | |||||
| bool _is_dynamic; | |||||
| size_t index { 0 }; | |||||
| std::ostringstream os; | |||||
| std::ostringstream join; | |||||
| select_query_builder_t( | |||||
| const table_t& p_table, | |||||
| const filter_t& p_filter, | |||||
| bool p_is_dynamic) | |||||
| : _table (p_table) | |||||
| , _filter (p_filter) | |||||
| , _is_dynamic(p_is_dynamic) | |||||
| { } | |||||
| inline void add_field(const field_t& field) | |||||
| { | |||||
| if (index++) os << ", "; | |||||
| os << "`" | |||||
| << field.table_name | |||||
| << "`.`" | |||||
| << field.field_name | |||||
| << "`"; | |||||
| } | |||||
| inline bool add_table(const table_t& table, const std::string& prefix) | |||||
| { | |||||
| bool ret = false; | |||||
| if (table.base_table) | |||||
| { | |||||
| auto tmp = add_table(*table.base_table, ""); | |||||
| if (tmp) | |||||
| { | |||||
| assert(table.base_table->primary_key_field); | |||||
| auto& base_key = *table.base_table->primary_key_field; | |||||
| ret = true; | |||||
| join << " LEFT JOIN `" | |||||
| << table.table_name | |||||
| << "` ON `" | |||||
| << table.table_name | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`=`" | |||||
| << base_key.table_name | |||||
| << "`.`" | |||||
| << base_key.field_name | |||||
| << "`"; | |||||
| } | |||||
| } | |||||
| /* __type */ | |||||
| if ( _is_dynamic | |||||
| && !table.base_table | |||||
| && !table.derived_tables.empty()) | |||||
| { | |||||
| if (index++) os << ", "; | |||||
| os << "`" | |||||
| << table.table_name | |||||
| << "`.`__type` AS `__type`"; | |||||
| ret = true; | |||||
| } | |||||
| if (_filter.is_excluded(table)) | |||||
| return ret; | |||||
| ret = true; | |||||
| /* primary key */ | |||||
| assert(table.primary_key_field); | |||||
| add_field(*table.primary_key_field); | |||||
| /* data fields */ | |||||
| for (auto& ptr : table.data_fields) | |||||
| { | |||||
| assert(ptr); | |||||
| auto& field = *ptr; | |||||
| if (!_filter.is_excluded(field)) | |||||
| add_field(field); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| inline std::string operator()() | |||||
| { | |||||
| os << "SELECT "; | |||||
| add_table(_table, ""); | |||||
| os << " FROM `" | |||||
| << _table.table_name | |||||
| << "`" | |||||
| << join.str() | |||||
| << " ?where! ?order! ?limit!"; | |||||
| return os.str(); | |||||
| } | |||||
| }; | |||||
| /* build queries */ | /* build queries */ | ||||
| std::string build_init_stage1_query(const table_t& table) | std::string build_init_stage1_query(const table_t& table) | ||||
| @@ -380,7 +547,7 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| /* base table key fields */ | /* base table key fields */ | ||||
| if ( static_cast<bool>(table.base_table) | if ( static_cast<bool>(table.base_table) | ||||
| && ( !is_update | && ( !is_update | ||||
| || filter->contains(table.base_table, true))) | |||||
| || !filter->is_excluded(*table.base_table))) | |||||
| { | { | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| @@ -401,11 +568,11 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| for (auto& ptr : table.foreign_table_one_fields) | for (auto& ptr : table.foreign_table_one_fields) | ||||
| { | { | ||||
| assert(static_cast<bool>(ptr)); | assert(static_cast<bool>(ptr)); | ||||
| if (is_update && !filter->contains(ptr)) | |||||
| auto& field_info = *ptr; | |||||
| if (is_update && filter->is_excluded(field_info)) | |||||
| continue; | continue; | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| auto& field_info = *ptr; | |||||
| assert(field_info.referenced_table); | assert(field_info.referenced_table); | ||||
| assert(field_info.referenced_table->primary_key_field); | assert(field_info.referenced_table->primary_key_field); | ||||
| auto& key_info = *field_info.referenced_table->primary_key_field; | auto& key_info = *field_info.referenced_table->primary_key_field; | ||||
| @@ -462,12 +629,12 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| /* data fields */ | /* data fields */ | ||||
| for (auto& ptr : table.data_fields) | for (auto& ptr : table.data_fields) | ||||
| { | { | ||||
| if (is_update && !filter->contains(ptr)) | |||||
| assert(ptr); | |||||
| auto& field_info = *ptr; | |||||
| if (is_update && filter->is_excluded(field_info)) | |||||
| continue; | continue; | ||||
| if (index++) | if (index++) | ||||
| os << ", "; | os << ", "; | ||||
| assert(static_cast<bool>(ptr)); | |||||
| auto& field_info = *ptr; | |||||
| os << "`" | os << "`" | ||||
| << field_info.field_name | << field_info.field_name | ||||
| << "`=" | << "`=" | ||||
| @@ -505,6 +672,14 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||||
| return os.str(); | return os.str(); | ||||
| } | } | ||||
| std::string build_select_query( | |||||
| const table_t& table, | |||||
| const filter_t& filter, | |||||
| bool is_dynamic) | |||||
| { | |||||
| return select_query_builder_t(table, filter, is_dynamic)(); | |||||
| } | |||||
| /* execute_create_update */ | /* execute_create_update */ | ||||
| std::string table_t::execute_create_update( | std::string table_t::execute_create_update( | ||||
| @@ -537,7 +712,7 @@ std::string table_t::execute_create_update( | |||||
| /* base_key */ | /* base_key */ | ||||
| if ( base_table | if ( base_table | ||||
| && ( !is_update | && ( !is_update | ||||
| || filter->contains(base_table, true))) | |||||
| || !filter->is_excluded(*base_table))) | |||||
| { | { | ||||
| auto new_context = context; | auto new_context = context; | ||||
| if (!new_context.derived_table) | if (!new_context.derived_table) | ||||
| @@ -547,14 +722,14 @@ std::string table_t::execute_create_update( | |||||
| ++index; | ++index; | ||||
| } | } | ||||
| if (is_update && !filter->contains(this, false)) | |||||
| if (is_update && filter->is_excluded(*this)) | |||||
| return primary_key; | return primary_key; | ||||
| /* foreign table one fields */ | /* foreign table one fields */ | ||||
| for (auto& ptr : foreign_table_one_fields) | for (auto& ptr : foreign_table_one_fields) | ||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| if (is_update && !filter->contains(ptr)) | |||||
| if (is_update && filter->is_excluded(*ptr)) | |||||
| continue; | continue; | ||||
| value_t key = ptr->foreign_create_update(context); | value_t key = ptr->foreign_create_update(context); | ||||
| if (key.has_value()) statement.set(index, std::move(key)); | if (key.has_value()) statement.set(index, std::move(key)); | ||||
| @@ -596,9 +771,9 @@ std::string table_t::execute_create_update( | |||||
| /* data fields */ | /* data fields */ | ||||
| for (auto& ptr : data_fields) | for (auto& ptr : data_fields) | ||||
| { | { | ||||
| if (is_update && !filter->contains(ptr)) | |||||
| continue; | |||||
| assert(ptr); | assert(ptr); | ||||
| if (is_update && filter->is_excluded(*ptr)) | |||||
| continue; | |||||
| auto& field_info = *ptr; | auto& field_info = *ptr; | ||||
| auto value = field_info.get(context); | auto value = field_info.get(context); | ||||
| @@ -654,8 +829,8 @@ std::string table_t::execute_create_update( | |||||
| { | { | ||||
| assert(ptr); | assert(ptr); | ||||
| if ( is_update | if ( is_update | ||||
| && ( !filter->contains(ptr) | |||||
| || !filter->contains(ptr->referenced_table, true))) | |||||
| && ( filter->is_excluded(*ptr) | |||||
| || filter->is_excluded(*ptr->referenced_table))) | |||||
| continue; | continue; | ||||
| auto next_context = context; | auto next_context = context; | ||||
| @@ -750,6 +925,20 @@ const table_t* table_t::get_derived(size_t id) const | |||||
| return *_statement_create_table; | return *_statement_create_table; | ||||
| } | } | ||||
| ::cppmariadb::statement& table_t::get_statement_select(const read_context& context) const | |||||
| { | |||||
| auto& map = context.is_dynamic | |||||
| ? _statement_select_dynamic | |||||
| : _statement_select_static; | |||||
| auto it = map.find(context.filter.cache_id); | |||||
| if (it == map.end()) | |||||
| { | |||||
| auto query = build_select_query(*this, context.filter, context.is_dynamic); | |||||
| it = map.emplace(context.filter.cache_id, ::cppmariadb::statement(query)).first; | |||||
| } | |||||
| return it->second; | |||||
| } | |||||
| std::string table_t::create_update_base(const create_update_context& context) const | std::string table_t::create_update_base(const create_update_context& context) const | ||||
| { | { | ||||
| throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { } | ||||
| @@ -780,4 +969,19 @@ std::string table_t::create_update_exec(const create_update_context& context) co | |||||
| } | } | ||||
| std::string table_t::create_update_intern(const create_update_context& context) const | std::string table_t::create_update_intern(const create_update_context& context) const | ||||
| { return create_update_exec(context); } | |||||
| { return create_update_exec(context); } | |||||
| void table_t::read_exec(const read_context& context) const | |||||
| { | |||||
| auto& statement = get_statement_select(context); | |||||
| auto& connection = context.connection; | |||||
| 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; | |||||
| while ((row = result->next())) | |||||
| data_extractor_t(*this, context, *row)(); | |||||
| context.finish(); | |||||
| } | |||||
| @@ -6,13 +6,27 @@ | |||||
| using namespace ::testing; | using namespace ::testing; | ||||
| using namespace ::cpphibernate; | using namespace ::cpphibernate; | ||||
| using namespace ::cpphibernate::modifier; | |||||
| using namespace ::boost::hana::literals; | |||||
| TEST(CppHibernateTests, read_test1) | TEST(CppHibernateTests, read_test1) | ||||
| { | { | ||||
| StrictMock<mariadb_mock> mock; | StrictMock<mariadb_mock> mock; | ||||
| // expect_query(mock, "START TRANSACTION"); | |||||
| // expect_query(mock, "COMMIT"); | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "`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` ", | |||||
| result_used({ | |||||
| { "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" } | |||||
| })); | |||||
| expect_query(mock, "COMMIT"); | |||||
| EXPECT_CALL( | EXPECT_CALL( | ||||
| mock, | mock, | ||||
| @@ -26,9 +40,105 @@ TEST(CppHibernateTests, read_test1) | |||||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | ||||
| test1 t1; | test1 t1; | ||||
| t1.id = uuid("1e133ad8-ad2e-11e8-98d0-529269fb1459"); | |||||
| t1.id = uuid("3d12697a-abb9-11e8-98d0-529269fb1459"); | |||||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | ||||
| auto context = make_context<driver::mariadb>(test_schema, connection); | auto context = make_context<driver::mariadb>(test_schema, connection); | ||||
| using namespace modifier; | |||||
| context.read(t1); | context.read(t1); | ||||
| } | |||||
| EXPECT_EQ (t1.str_data, "str_data of class `test1` object `t1`"); | |||||
| EXPECT_EQ (t1.str64_data, "str64_data of class `test1` object `t1`"); | |||||
| EXPECT_FALSE(static_cast<bool>(t1.u32_nullable)); | |||||
| ASSERT_TRUE (static_cast<bool>(t1.u32_ptr_u)); | |||||
| EXPECT_EQ (*t1.u32_ptr_u, 123); | |||||
| ASSERT_TRUE (static_cast<bool>(t1.u32_ptr_s)); | |||||
| EXPECT_EQ (*t1.u32_ptr_s, 456); | |||||
| } | |||||
| TEST(CppHibernateTests, read_test2) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "`tbl_test2`.`tbl_test2_id`, " | |||||
| "`tbl_test2`.`u8_data`, " | |||||
| "`tbl_test2`.`i8_data`, " | |||||
| "`tbl_test2`.`u16_data`, " | |||||
| "`tbl_test2`.`i16_data` " | |||||
| "FROM " | |||||
| "`tbl_test2` ", | |||||
| result_used({ | |||||
| { "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" } | |||||
| })); | |||||
| 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) test2_key_field = test_schema.tables[1_c].fields[0_c]; | |||||
| test2 t2; | |||||
| context.read(t2, where(equal(test2_key_field, "3d1270dc-abb9-11e8-98d0-529269fb1459"))); | |||||
| EXPECT_EQ(1, t2.u8_data); | |||||
| EXPECT_EQ(2, t2.i8_data); | |||||
| EXPECT_EQ(3, t2.u16_data); | |||||
| EXPECT_EQ(4, t2.i16_data); | |||||
| } | |||||
| TEST(CppHibernateTests, read_test3) | |||||
| { | |||||
| StrictMock<mariadb_mock> mock; | |||||
| expect_query(mock, "START TRANSACTION"); | |||||
| expect_query(mock, "SELECT " | |||||
| "`tbl_test3`.`tbl_test3_id`, " | |||||
| "`tbl_test3`.`u32_data`, " | |||||
| "`tbl_test3`.`i32_data`, " | |||||
| "`tbl_test3`.`u64_data`, " | |||||
| "`tbl_test3`.`i64_data` " | |||||
| "FROM " | |||||
| "`tbl_test3` ", | |||||
| result_used({ | |||||
| { "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" } | |||||
| })); | |||||
| 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); | |||||
| test3 t3; | |||||
| t3.id = uuid("3d12737a-abb9-11e8-98d0-529269fb1459"); | |||||
| context.read(t3); | |||||
| EXPECT_EQ(5, t3.u32_data); | |||||
| EXPECT_EQ(6, t3.i32_data); | |||||
| EXPECT_EQ(7, t3.u64_data); | |||||
| EXPECT_EQ(8, t3.i64_data); | |||||
| } | |||||
| @@ -24,7 +24,7 @@ ACTION(EscapeString) | |||||
| struct result_data | struct result_data | ||||
| : public mariadb_mock_item | : public mariadb_mock_item | ||||
| { | { | ||||
| using row_type = std::vector<std::string>; | |||||
| using row_type = std::vector<const char *>; | |||||
| using data_type = std::vector<row_type>; | using data_type = std::vector<row_type>; | ||||
| struct internal_data_t | struct internal_data_t | ||||
| @@ -55,8 +55,8 @@ struct result_data | |||||
| for (size_t j = 0; j < d.size(); ++j) | for (size_t j = 0; j < d.size(); ++j) | ||||
| { | { | ||||
| auto& str = d.at(j); | auto& str = d.at(j); | ||||
| intern.data[j] = const_cast<char*>(str.c_str()); | |||||
| intern.length[j] = static_cast<unsigned long>(str.size()); | |||||
| intern.data[j] = const_cast<char*>(str); | |||||
| intern.length[j] = static_cast<unsigned long>(str ? strlen(str) : 0); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||