* added index database field for sorted containers (like vector or list) * started to implement mariadb read methodsmaster
| @@ -53,13 +53,14 @@ beg_namespace_cpphibernate | |||
| template<typename T_dataset, typename... T_modifiers> | |||
| constexpr auto read(T_dataset& dataset, T_modifiers&&... modifiers) | |||
| -> mp::enable_if< | |||
| modifier::all_are_modifier<mp::decay_t<T_modifiers>...>> | |||
| modifier::all_are_modifiers<mp::decay_t<T_modifiers>...>> | |||
| { | |||
| using namespace modifier; | |||
| using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
| schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
| this->read_impl(dataset, hana::make_tuple(std::forward<T_modifiers>(modifiers)...)); | |||
| this->read_impl(dataset, modifier::make_list(std::forward<T_modifiers>(modifiers)...)); | |||
| } | |||
| template<typename T_dataset> | |||
| constexpr auto read(T_dataset& dataset) | |||
| -> mp::enable_if_c< | |||
| @@ -70,7 +71,7 @@ beg_namespace_cpphibernate | |||
| using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
| auto& table = schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
| auto& primary_key = schema::table::get_primary_key_field(table); | |||
| this->read_impl(dataset, hana::make_tuple(where(equal(primary_key, primary_key.getter(dataset))))); | |||
| this->read_impl(dataset, modifier::make_list(where(equal(primary_key, primary_key.getter(dataset))))); | |||
| } | |||
| template<typename T_dataset> | |||
| @@ -82,7 +83,7 @@ beg_namespace_cpphibernate | |||
| using namespace modifier; | |||
| using real_dataset_type = misc::real_dataset_t<mp::decay_t<T_dataset>>; | |||
| schema::tables::find(_schema.tables, hana::type_c<real_dataset_type>); | |||
| this->read_impl(dataset, hana::make_tuple()); | |||
| this->read_impl(dataset, modifier::make_list()); | |||
| } | |||
| /* update */ | |||
| @@ -124,6 +124,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| const table_t* derived_table; | |||
| const field_t* owner_field; | |||
| std::string owner_key; | |||
| ssize_t index; | |||
| template<typename T_data> | |||
| inline create_update_context( | |||
| @@ -136,6 +137,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| , is_update (p_is_update) | |||
| , derived_table (nullptr) | |||
| , owner_field (nullptr) | |||
| , index (-1) | |||
| { } | |||
| }; | |||
| @@ -211,7 +211,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| }; | |||
| template<typename T> | |||
| struct type_properties<T, mp::enable_if<misc::is_nullable<mp::clean_type<T>>>> | |||
| struct type_properties<T, mp::enable_if<misc::is_nullable<mp::decay_t<T>>>> | |||
| { | |||
| using nullable_type = T; | |||
| using nullable_helper_type = misc::nullable_helper<nullable_type>; | |||
| @@ -1,3 +1,5 @@ | |||
| #pragma once | |||
| #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/where.h> | |||
| @@ -99,11 +99,14 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| auto& dataset = context.get<dataset_type>(); | |||
| transaction_lock trans(connection); | |||
| ssize_t index = 0; | |||
| for (auto& x : dataset) | |||
| { | |||
| using new_dataset_type = mp::decay_t<decltype(x)>; | |||
| using new_create_update_impl_type = create_update_impl_t<new_dataset_type>; | |||
| new_create_update_impl_type::apply(change_context(context, x), strict); | |||
| auto new_context = change_context(context, x); | |||
| new_context.index = index++; | |||
| new_create_update_impl_type::apply(new_context, strict); | |||
| } | |||
| trans.commit(); | |||
| return ret; | |||
| @@ -0,0 +1,65 @@ | |||
| #pragma once | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/modifier.h> | |||
| #include <cpphibernate/driver/mariadb/impl/modifier_tags.h> | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| /* limit_builder */ | |||
| template<typename T_modifiers> | |||
| struct limit_builder | |||
| { | |||
| ::cppmariadb::statement statement; | |||
| limit_builder(const T_modifiers& modifier) | |||
| { | |||
| ssize_t limit = -1; | |||
| ssize_t offset = -1; | |||
| hana::for_each(modifier, [&limit, &offset](auto& modifier){ | |||
| using modifier_type = mp::decay_t<decltype(modifier)>; | |||
| using is_limit_type = modifier::is_limit_modifier<modifier_type>; | |||
| using is_offset_type = modifier::is_offset_modifier<modifier_type>; | |||
| hana::eval_if( | |||
| is_limit_type { }, | |||
| [&limit, &modifier](auto _){ | |||
| limit = hana::value(_(modifier).value); | |||
| }, | |||
| [&offset, &modifier](){ | |||
| hana::eval_if( | |||
| is_offset_type { }, | |||
| [&offset, &modifier](auto _){ | |||
| offset = hana::value(_(modifier).value); | |||
| }, | |||
| []{ | |||
| /* no-op */ | |||
| }); | |||
| }); | |||
| }); | |||
| if (offset >= 0 && limit < 0) | |||
| limit = 1000000; | |||
| if (limit >= 0) | |||
| { | |||
| std::ostringstream ss; | |||
| ss << "LIMIT " << limit; | |||
| if (offset >= 0) | |||
| ss << " OFFSET" << offset; | |||
| statement.assign(ss.str()); | |||
| } | |||
| } | |||
| }; | |||
| template<typename T_modifiers> | |||
| inline ::cppmariadb::statement& build_limit(const T_modifiers& modifiers) | |||
| { | |||
| static limit_builder<T_modifiers> builder(modifiers); | |||
| return builder.statement; | |||
| } | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -0,0 +1,69 @@ | |||
| #pragma once | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/modifier.h> | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| namespace __impl | |||
| { | |||
| /* make_modifier_tags_impl */ | |||
| template<typename X, typename = void> | |||
| struct make_modifier_tag_impl | |||
| { | |||
| template <typename ...T_args> | |||
| static constexpr decltype(auto) apply(T_args&&... args) | |||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for make_modifier_tag(...)!"); } | |||
| }; | |||
| template<typename T_modifier> | |||
| struct make_modifier_tag_impl< | |||
| mp::list<T_modifier>, | |||
| mp::enable_if_c< | |||
| modifier::is_limit_modifier<mp::decay_t<T_modifier>>::value | |||
| || modifier::is_offset_modifier<mp::decay_t<T_modifier>>::value>> | |||
| { | |||
| static constexpr decltype(auto) apply(T_modifier&&) | |||
| { return T_modifier { }; } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_modifier_tag = misc::make_generic_predicate<__impl::make_modifier_tag_impl> { }; | |||
| namespace __impl | |||
| { | |||
| /* make_modifier_tags_impl */ | |||
| template<typename X, typename = void> | |||
| struct make_modifier_tags_impl | |||
| { | |||
| template <typename ...T_args> | |||
| static constexpr decltype(auto) apply(T_args&&... args) | |||
| { static_assert(sizeof...(args) == -1, "Invalid parameters for make_modifier_tags(...)!"); } | |||
| }; | |||
| template<typename T_modifiers> | |||
| struct make_modifier_tags_impl< | |||
| mp::list<T_modifiers>, | |||
| mp::enable_if_c< | |||
| modifier::is_modifiers<mp::decay_t<T_modifiers>>::value>> | |||
| { | |||
| static constexpr decltype(auto) apply(T_modifiers&& modifiers) | |||
| { | |||
| return hana::transform( | |||
| modifiers, | |||
| make_modifier_tag); | |||
| } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_modifier_tags = misc::make_generic_predicate<__impl::make_modifier_tags_impl> { }; | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -0,0 +1,183 @@ | |||
| #pragma once | |||
| #include <sstream> | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/modifier.h> | |||
| #include <cpphibernate/driver/mariadb/schema/schema.h> | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| /* where_builder */ | |||
| template<typename T_modifiers> | |||
| struct where_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) | |||
| { } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_and<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "("; | |||
| build_clause(os, clause.clauses[hana::size_c<0>]); | |||
| os << ")"; | |||
| hana::for_each( | |||
| hana::remove_at(clause.clauses, hana::size_c<0>), | |||
| [&](auto& clause) { | |||
| os << " AND ("; | |||
| build_clause(os, clause); | |||
| os << ")"; | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_or<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "("; | |||
| build_clause(os, clause.clauses[hana::size_c<0>]); | |||
| os << ")"; | |||
| hana::for_each( | |||
| hana::remove_at(clause.clauses, hana::size_c<0>), | |||
| [&](auto& clause) { | |||
| os << " OR ("; | |||
| build_clause(os, clause); | |||
| os << ")"; | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "NOT ("; | |||
| build_clause(os, clause.clause); | |||
| os << ")"; | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
| { | |||
| auto field_id = misc::get_type_id(hana::type_c<mp::decay_t<decltype(clause.field)>>); | |||
| auto& field = schema.field(field_id); | |||
| os << "`" | |||
| << field.table_name | |||
| << "`.`" | |||
| << field.field_name | |||
| << "`=" | |||
| << field.convert_to_open | |||
| << "?\?" | |||
| << field.convert_to_close; | |||
| } | |||
| 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_where_type = modifier::is_where_modifier<modifier_type>; | |||
| hana::eval_if( | |||
| is_where_type { }, | |||
| [&](auto _){ | |||
| if (index++ == 0) os << "WHERE ("; | |||
| else os << " AND ("; | |||
| build_clause(_(modifier).clause); | |||
| os << ")"; | |||
| }, | |||
| []{ }); | |||
| }); | |||
| os << ")"; | |||
| statement.assign(os.str()); | |||
| } | |||
| }; | |||
| struct assign_t | |||
| { | |||
| ::cppmariadb::statement& statement; | |||
| const T_modifiers& modifiers; | |||
| size_t index { 0 }; | |||
| inline assign_t(::cppmariadb::statement& p_statement, const T_modifiers& p_modifiers) | |||
| : statement(p_statement) | |||
| , modifiers(p_modifiers) | |||
| { } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| -> mp::enable_if_c< | |||
| modifier::is_where_clause_and<mp::decay_t<T_clause>>::value | |||
| || modifier::is_where_clause_or <mp::decay_t<T_clause>>::value> | |||
| { | |||
| hana::for_each([&](auto& clause) { | |||
| assign_clause(clause); | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
| { | |||
| assign_clause(clause.clause); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| -> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
| { | |||
| statement.set(index, clause.value); | |||
| ++index; | |||
| } | |||
| inline void assign() | |||
| { | |||
| hana::for_each(modifiers, [&](auto& modifier){ | |||
| using modifier_type = mp::decay_t<decltype(modifier)>; | |||
| using is_where_type = modifier::is_where_modifier<modifier_type>; | |||
| hana::eval_if( | |||
| is_where_type { }, | |||
| [&](auto _){ | |||
| assign_clause(_(modifier).clause); | |||
| }, | |||
| []{ }); | |||
| }); | |||
| } | |||
| }; | |||
| 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; | |||
| } | |||
| assign_t(_statement, modifiers).assign(); | |||
| return _statement; | |||
| } | |||
| }; | |||
| template<typename T_modifiers> | |||
| inline decltype(auto) build_where(const schema_t& schema, const T_modifiers& modifiers) | |||
| { | |||
| static where_builder<T_modifiers> builder; | |||
| return builder.assign(schema, modifiers); | |||
| } | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -40,6 +40,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| create_update_impl_t<T_dataset>::apply( | |||
| create_update_context(dataset, _schema, _connection, _filter, false)); | |||
| } | |||
| template<typename T_dataset, typename T_modifiers> | |||
| 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; | |||
| } | |||
| }; | |||
| } | |||
| @@ -17,104 +17,128 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| struct field_t | |||
| { | |||
| size_t table_dataset_id { 0 }; | |||
| size_t value_dataset_id { 0 }; | |||
| bool value_is_nullable { false }; | |||
| bool value_is_container { false }; | |||
| std::string schema_name; | |||
| std::string table_name; | |||
| std::string field_name; | |||
| attributes_t attributes; | |||
| const table_t* table { nullptr }; | |||
| const table_t* referenced_table { nullptr }; | |||
| size_t id { 0 }; // unique id of the field | |||
| size_t dataset_id { 0 }; // unique id of the dataset type | |||
| size_t real_dataset_id { 0 }; // unique id of the real/unwrapped dataset type | |||
| size_t value_id { 0 }; // unique id of the value type | |||
| size_t real_value_id { 0 }; // unique id of the real/unwrapped value type | |||
| bool value_is_nullable { false }; // value is stored in a nullable container | |||
| bool value_is_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 | |||
| const table_t* table { nullptr }; // table this field belongs to | |||
| const table_t* referenced_table { nullptr }; // table that belongs to the value (if exists) | |||
| std::string schema_name; // name of the SQL schema | |||
| std::string table_name; // name of the SQL table | |||
| std::string field_name; // name of the SQL field | |||
| std::string type; // SQL type name | |||
| std::string create_arguments; // additional arguments for CREATE TABLE command | |||
| std::string convert_to_open; // SQL code to open the "convert to" operation | |||
| std::string convert_to_close; // SQL code to close the "convert to" operation | |||
| std::string convert_from_open; // SQL code to open the "convert from" operation | |||
| std::string convert_from_close; // SQL code to close the "convert from" operation | |||
| attributes_t attributes; // attributes for the field | |||
| inline field_t() = default; | |||
| inline field_t(const field_t&) = delete; | |||
| inline field_t(field_t&& other) | |||
| : table_dataset_id (std::move(other).table_dataset_id) | |||
| , value_dataset_id (std::move(other).value_dataset_id) | |||
| , value_is_nullable (std::move(other).value_is_nullable) | |||
| , value_is_container(std::move(other).value_is_container) | |||
| , schema_name (std::move(other).schema_name) | |||
| , table_name (std::move(other).table_name) | |||
| , field_name (std::move(other).field_name) | |||
| , attributes (std::move(other).attributes) | |||
| , table (nullptr) | |||
| , referenced_table (nullptr) | |||
| : id (std::move(other).id) | |||
| , dataset_id (std::move(other).dataset_id) | |||
| , real_dataset_id (std::move(other).real_dataset_id) | |||
| , 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_container (std::move(other).value_is_container) | |||
| , value_is_auto_incremented (std::move(other).value_is_auto_incremented) | |||
| , table (nullptr) | |||
| , referenced_table (nullptr) | |||
| , schema_name (std::move(other).schema_name) | |||
| , table_name (std::move(other).table_name) | |||
| , field_name (std::move(other).field_name) | |||
| , type (std::move(other).type) | |||
| , create_arguments (std::move(other).create_arguments) | |||
| , convert_to_open (std::move(other).convert_to_open) | |||
| , convert_to_close (std::move(other).convert_to_close) | |||
| , convert_from_open (std::move(other).convert_from_open) | |||
| , convert_from_close (std::move(other).convert_from_close) | |||
| , attributes (std::move(other).attributes) | |||
| { } | |||
| virtual ~field_t() { }; | |||
| void print(std::ostream& os) const; | |||
| void print (std::ostream& os) const; | |||
| virtual void update (); | |||
| /* CRUD */ | |||
| virtual value_t foreign_create_update(const create_update_context& context) const; | |||
| virtual value_t foreign_create_update (const create_update_context& context) const; | |||
| /* properties */ | |||
| virtual std::string type () const; | |||
| virtual std::string create_table_arguments () const; | |||
| virtual std::string generate_value (::cppmariadb::connection& connection) const; | |||
| virtual bool is_auto_generated () const; | |||
| virtual bool is_default (const data_context& context) const; | |||
| virtual std::string convert_to_open () const; | |||
| virtual std::string convert_to_close () const; | |||
| virtual std::string convert_from_open () const; | |||
| virtual std::string convert_from_close () const; | |||
| virtual value_t get (const data_context& context) const; | |||
| virtual void set (const data_context& context, const value_t&) const; | |||
| virtual bool is_default (const data_context& context) const; | |||
| virtual std::string generate_value (::cppmariadb::connection& connection) const; | |||
| }; | |||
| /* simple_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| template<typename T_field> | |||
| struct simple_field_t | |||
| : public field_t | |||
| { | |||
| using schema_type = T_schema; | |||
| using field_type = T_field; | |||
| using getter_type = typename mp::decay_t<field_type>::getter_type; | |||
| using dataset_type = typename getter_type::dataset_type; | |||
| using value_type = typename getter_type::value_type; | |||
| const schema_type& schema; | |||
| const field_type& field; | |||
| inline simple_field_t(const schema_type& p_schema, const field_type& p_field) | |||
| using base_type = field_t; | |||
| using field_type = T_field; | |||
| using getter_type = typename mp::decay_t<field_type>::getter_type; | |||
| using dataset_type = typename getter_type::dataset_type; | |||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
| using value_type = typename getter_type::value_type; | |||
| using real_value_type = misc::real_dataset_t<value_type>; | |||
| using type_props = type_properties<value_type>; | |||
| const field_type& field; | |||
| inline simple_field_t(const field_type& p_field) | |||
| : field_t () | |||
| , schema (p_schema) | |||
| , field (p_field) | |||
| { } | |||
| virtual void update() override; | |||
| }; | |||
| /* value_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| template<typename T_field> | |||
| struct value_field_t | |||
| : public simple_field_t<T_schema, T_field> | |||
| : public simple_field_t<T_field> | |||
| { | |||
| using base_type = simple_field_t<T_schema, T_field>; | |||
| using schema_type = T_schema; | |||
| using field_type = T_field; | |||
| using getter_type = typename base_type::getter_type; | |||
| using dataset_type = typename base_type::dataset_type; | |||
| using value_type = typename base_type::value_type; | |||
| using type_props = type_properties<value_type>; | |||
| using base_type = simple_field_t<T_field>; | |||
| using field_type = typename base_type::field_type; | |||
| using getter_type = typename base_type::getter_type; | |||
| using dataset_type = typename base_type::dataset_type; | |||
| using real_dataset_type = typename base_type::dataset_type; | |||
| using value_type = typename base_type::value_type; | |||
| using real_value_type = typename base_type::real_value_type; | |||
| using type_props = typename base_type::type_props; | |||
| using base_type::base_type; | |||
| virtual std::string type() const override; | |||
| virtual value_t get (const data_context& context) const override; | |||
| virtual void set (const data_context& context, const value_t&) const override; | |||
| static_assert(mp::is_same<dataset_type, real_dataset_type>::value, "internal error: dataset type mismatch!"); | |||
| virtual void update () override; | |||
| virtual value_t get (const data_context& context) const override; | |||
| virtual void set (const data_context& context, const value_t&) const override; | |||
| }; | |||
| /* primary_key_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| template<typename T_field> | |||
| struct primary_key_field_t | |||
| : public value_field_t<T_schema, T_field> | |||
| : public value_field_t<T_field> | |||
| { | |||
| using base_type = value_field_t<T_schema, T_field>; | |||
| using schema_type = typename base_type::schema_type; | |||
| using base_type = value_field_t<T_field>; | |||
| using field_type = typename base_type::field_type; | |||
| using dataset_type = typename base_type::dataset_type; | |||
| using value_type = typename base_type::value_type; | |||
| @@ -122,35 +146,29 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| using base_type::base_type; | |||
| virtual std::string create_table_arguments () const override; | |||
| virtual std::string generate_value (::cppmariadb::connection& connection) const override; | |||
| virtual bool is_auto_generated () const override; | |||
| virtual bool is_default (const data_context& context) const override; | |||
| virtual std::string convert_to_open () const override; | |||
| virtual std::string convert_to_close () const override; | |||
| virtual std::string convert_from_open () const override; | |||
| virtual std::string convert_from_close () const override; | |||
| virtual bool is_default (const data_context& context) const override; | |||
| virtual std::string generate_value(::cppmariadb::connection& connection) const override; | |||
| }; | |||
| /* data_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| template<typename T_field> | |||
| struct data_field_t | |||
| : public value_field_t<T_schema, T_field> | |||
| : public value_field_t<T_field> | |||
| { | |||
| using base_type = value_field_t<T_schema, T_field>; | |||
| using base_type = value_field_t<T_field>; | |||
| using base_type::base_type; | |||
| }; | |||
| /* foreign_table_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| template<typename T_field> | |||
| struct foreign_table_field_t | |||
| : public simple_field_t<T_schema, T_field> | |||
| : public simple_field_t<T_field> | |||
| { | |||
| public: | |||
| using base_type = simple_field_t<T_schema, T_field>; | |||
| using base_type = simple_field_t<T_field>; | |||
| using dataset_type = typename base_type::dataset_type; | |||
| using base_type::base_type; | |||
| @@ -6,21 +6,46 @@ | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| /* simple_field_t */ | |||
| template<typename T_field> | |||
| void simple_field_t<T_field> | |||
| ::update() | |||
| { | |||
| base_type::update(); | |||
| id = misc::get_type_id(hana::type_c<field_type>); | |||
| dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
| real_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
| value_id = misc::get_type_id(hana::type_c<value_type>); | |||
| real_value_id = misc::get_type_id(hana::type_c<real_value_type>); | |||
| value_is_nullable = misc::is_nullable<value_type>::value; | |||
| value_is_container = misc::is_container<value_type>::value; | |||
| value_is_ordered = misc::is_ordered<value_type>::value; | |||
| } | |||
| /* value_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| std::string value_field_t<T_schema, T_field>::type() const | |||
| { return type_props::type(); } | |||
| template<typename T_field> | |||
| void value_field_t<T_field> | |||
| ::update() | |||
| { | |||
| base_type::update(); | |||
| this->type = type_props::type(); | |||
| } | |||
| template<typename T_schema, typename T_field> | |||
| value_t value_field_t<T_schema, T_field>::get(const data_context& context) const | |||
| template<typename T_field> | |||
| value_t value_field_t<T_field> | |||
| ::get(const data_context& context) const | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| return type_props::convert_from(this->field.getter(dataset)); | |||
| } | |||
| template<typename T_schema, typename T_field> | |||
| void value_field_t<T_schema, T_field>::set(const data_context& context, const value_t& value) const | |||
| template<typename T_field> | |||
| void value_field_t<T_field> | |||
| ::set(const data_context& context, const value_t& value) const | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| this->field.setter(dataset, type_props::convert_to(value)); | |||
| @@ -28,12 +53,16 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| /* primary_key_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field>::create_table_arguments() const | |||
| { return key_props::create_table_argument; } | |||
| template<typename T_field> | |||
| bool primary_key_field_t<T_field> | |||
| ::is_default(const data_context& context) const | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| return key_props::is_default(this->field.getter(dataset)); | |||
| } | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field> | |||
| template<typename T_field> | |||
| std::string primary_key_field_t<T_field> | |||
| ::generate_value(::cppmariadb::connection& connection) const | |||
| { | |||
| auto ret = connection.execute_used(key_props::create_key_query); | |||
| @@ -42,37 +71,10 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| return ret->current()->at(0).template get<std::string>(); | |||
| } | |||
| template<typename T_schema, typename T_field> | |||
| bool primary_key_field_t<T_schema, T_field>::is_auto_generated() const | |||
| { return key_props::auto_generated::value; } | |||
| template<typename T_schema, typename T_field> | |||
| bool primary_key_field_t<T_schema, T_field>::is_default(const data_context& context) const | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| return key_props::is_default(this->field.getter(dataset)); | |||
| } | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field>::convert_to_open() const | |||
| { return key_props::convert_to_open; } | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field>::convert_to_close() const | |||
| { return key_props::convert_to_close; } | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field>::convert_from_open() const | |||
| { return key_props::convert_from_open; } | |||
| template<typename T_schema, typename T_field> | |||
| std::string primary_key_field_t<T_schema, T_field>::convert_from_close() const | |||
| { return key_props::convert_from_close; } | |||
| /* foreign_table_field_t */ | |||
| template<typename T_schema, typename T_field> | |||
| value_t foreign_table_field_t<T_schema, T_field> | |||
| template<typename T_field> | |||
| value_t foreign_table_field_t<T_field> | |||
| ::foreign_create_update(const create_update_context& context) const | |||
| { | |||
| auto& dataset = context.get<dataset_type>(); | |||
| @@ -114,15 +116,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| template<typename T_schema, typename T_field, typename = void> | |||
| struct field_type | |||
| { using type = data_field_t<T_schema, T_field>; }; | |||
| { using type = data_field_t<T_field>; }; | |||
| template<typename T_schema, typename T_field> | |||
| struct field_type<T_schema, T_field, mp::enable_if<is_primary_key_field<T_field>>> | |||
| { using type = primary_key_field_t<T_schema, T_field>; }; | |||
| { using type = primary_key_field_t<T_field>; }; | |||
| template<typename T_schema, typename T_field> | |||
| struct field_type<T_schema, T_field, mp::enable_if<is_foreign_table_field<T_schema, T_field>>> | |||
| { using type = foreign_table_field_t<T_schema, T_field>; }; | |||
| { using type = foreign_table_field_t<T_field>; }; | |||
| template<typename T_schema, typename T_field> | |||
| using field_type_t = typename field_type<T_schema, T_field>::type; | |||
| @@ -149,17 +151,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| using schema_type = mp::decay_t<T_schema>; | |||
| using field_type = mp::decay_t<T_field>; | |||
| using getter_type = mp::decay_t<typename field_type::getter_type>; | |||
| using value_type = mp::decay_t<typename getter_type::value_type>; | |||
| using dataset_type = mp::decay_t<typename getter_type::dataset_type>; | |||
| using value_dataset_type = misc::real_dataset_t<value_type>; | |||
| using return_type = field_type_t<schema_type, field_type>; | |||
| using primary_key_type = primary_key_field_t<schema_type, field_type>; | |||
| return_type ret(schema, field); | |||
| ret.table_dataset_id = misc::get_type_id(hana::type_c<dataset_type>); | |||
| ret.value_dataset_id = misc::get_type_id(hana::type_c<value_dataset_type>); | |||
| ret.value_is_nullable = misc::is_nullable<value_type>::value; | |||
| ret.value_is_container = misc::is_container<value_type>::value; | |||
| using primary_key_type = primary_key_field_t<field_type>; | |||
| return_type ret(field); | |||
| ret.schema_name = schema.name; | |||
| ret.table_name = table.name; | |||
| ret.field_name = field.name; | |||
| @@ -27,6 +27,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| void print (std::ostream& os) const; | |||
| const table_t& table(size_t dataset_id) const; | |||
| const field_t& field(size_t field_id) const; | |||
| /* CRUD */ | |||
| void init(const init_context& context) const; | |||
| @@ -150,8 +150,11 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| std::declval<T_table>().wrapped_dataset))>; | |||
| using wrapped_dataset_type = typename mp::decay_t<T_table>::wrapped_dataset_type; | |||
| using dataset_type = misc::unwrap_t<wrapped_dataset_type>; | |||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
| using table_type = table_type_t<dataset_type, base_type>; | |||
| static_assert(mp::is_same<dataset_type, real_dataset_type>::value, "table cn only be created for simple dataset types (not for containers)!"); | |||
| table_type ret(schema, table); | |||
| ret.dataset_id = misc::get_type_id(table.wrapped_dataset); | |||
| ret.base_dataset_id = misc::get_type_id(wrapped_base_type { }); | |||
| @@ -0,0 +1,22 @@ | |||
| #pragma once | |||
| #include <set> | |||
| #include <list> | |||
| #include <vector> | |||
| #include <cpphibernate/config.h> | |||
| beg_namespace_cpphibernate_misc | |||
| { | |||
| /* container_helper */ | |||
| template<typename T_container, typename = void> | |||
| struct container_helper | |||
| { | |||
| using container_type = T_container; | |||
| using value_type = real_dataset_t<container_type>; | |||
| }; | |||
| } | |||
| end_namespace_cpphibernate_misc | |||
| @@ -22,13 +22,13 @@ beg_namespace_cpphibernate_misc | |||
| : public mp::c_false_t | |||
| { }; | |||
| template<typename T> | |||
| struct is_container_impl<std::list<T>> | |||
| template<typename T, typename... T_args> | |||
| struct is_container_impl<std::list<T, T_args...>> | |||
| : public mp::c_true_t | |||
| { }; | |||
| template<typename T> | |||
| struct is_container_impl<std::vector<T>> | |||
| template<typename T, typename... T_args> | |||
| struct is_container_impl<std::vector<T, T_args...>> | |||
| : public mp::c_true_t | |||
| { }; | |||
| @@ -71,6 +71,23 @@ beg_namespace_cpphibernate_misc | |||
| : public mp::c_true_t | |||
| { }; | |||
| /* is_ordered_impl */ | |||
| template<typename T> | |||
| struct is_ordered_impl | |||
| : public mp::c_false_t | |||
| { }; | |||
| template<typename T, typename... T_args> | |||
| struct is_ordered_impl<std::list<T, T_args...>> | |||
| : public mp::c_true_t | |||
| { }; | |||
| template<typename T, typename... T_args> | |||
| struct is_ordered_impl<std::vector<T, T_args...>> | |||
| : public mp::c_true_t | |||
| { }; | |||
| /* real_dataset_impl */ | |||
| template<typename T, typename = void> | |||
| @@ -81,22 +98,21 @@ beg_namespace_cpphibernate_misc | |||
| struct real_dataset_impl<utl::nullable<T>, void> | |||
| { using type = typename real_dataset_impl<T>::type; }; | |||
| template<typename T> | |||
| struct real_dataset_impl<std::unique_ptr<T>, void> | |||
| template<typename T, typename... T_args> | |||
| struct real_dataset_impl<std::unique_ptr<T, T_args...>, void> | |||
| { using type = typename real_dataset_impl<T>::type; }; | |||
| template<typename T> | |||
| struct real_dataset_impl<std::shared_ptr<T>, void> | |||
| { using type = typename real_dataset_impl<T>::type; }; | |||
| template<typename T> | |||
| struct real_dataset_impl<std::vector<T>, void> | |||
| template<typename T, typename... T_args> | |||
| struct real_dataset_impl<std::list<T, T_args...>, void> | |||
| { using type = typename real_dataset_impl<T>::type; }; | |||
| template<typename T> | |||
| struct real_dataset_impl<std::list<T>, void> | |||
| template<typename T, typename... T_args> | |||
| struct real_dataset_impl<std::vector<T, T_args...>, void> | |||
| { using type = typename real_dataset_impl<T>::type; }; | |||
| } | |||
| /* meta */ | |||
| @@ -111,6 +127,11 @@ beg_namespace_cpphibernate_misc | |||
| : public __impl::is_nullable_impl<T> | |||
| { }; | |||
| template<typename T> | |||
| struct is_ordered | |||
| : public __impl::is_ordered_impl<T> | |||
| { }; | |||
| template<typename T> | |||
| struct is_pointer | |||
| : public __impl::is_pointer_impl<T> | |||
| @@ -2,5 +2,6 @@ | |||
| #include <cpphibernate/modifier/limit.h> | |||
| #include <cpphibernate/modifier/modifier.h> | |||
| #include <cpphibernate/modifier/modifiers.h> | |||
| #include <cpphibernate/modifier/offset.h> | |||
| #include <cpphibernate/modifier/where.h> | |||
| @@ -23,7 +23,7 @@ beg_namespace_cpphibernate_modifier | |||
| { }; | |||
| template<typename... T> | |||
| struct all_are_modifier | |||
| struct all_are_modifiers | |||
| : public mp::all_true<is_modifier<T>::value...> | |||
| { }; | |||
| @@ -0,0 +1,65 @@ | |||
| #pragma once | |||
| #include <cpphibernate/misc.h> | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/modifier/modifier.h> | |||
| beg_namespace_cpphibernate_modifier | |||
| { | |||
| namespace __impl | |||
| { | |||
| /* is_modifiers_impl */ | |||
| template<typename T, typename = void> | |||
| struct is_modifiers_impl | |||
| : mp::c_false_t | |||
| { }; | |||
| template<typename... T> | |||
| struct is_modifiers_impl<hana::basic_tuple<T...>, mp::enable_if<all_are_modifiers<T...>>> | |||
| : mp::c_true_t | |||
| { }; | |||
| } | |||
| /* meta */ | |||
| template<typename T> | |||
| struct is_modifiers | |||
| : public __impl::is_modifiers_impl<T> | |||
| { }; | |||
| /* operations */ | |||
| namespace __impl | |||
| { | |||
| /* make_modifiers_impl */ | |||
| template<typename T, typename = void> | |||
| struct make_modifiers_impl | |||
| { | |||
| template<typename... T_args> | |||
| static constexpr decltype(auto) apply(T_args&&...) | |||
| { static_assert(sizeof...(T_args) == -1, "Invalid parameters for hibernate::schema::modifier::make(...)!"); } | |||
| }; | |||
| template<typename... T> | |||
| struct make_modifiers_impl< | |||
| mp::list<T...>, | |||
| mp::enable_if_c< | |||
| all_are_modifiers<mp::decay_t<T>...>::value>> | |||
| { | |||
| template<typename... T_args> | |||
| static constexpr decltype(auto) apply(T_args&&... args) | |||
| { return hana::make_basic_tuple(std::forward<T_args>(args)...); } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_list = misc::make_generic_predicate<__impl::make_modifiers_impl> { }; | |||
| } | |||
| end_namespace_cpphibernate_modifier | |||
| @@ -3,4 +3,5 @@ | |||
| #include <cpphibernate/modifier/where/clauses/and.h> | |||
| #include <cpphibernate/modifier/where/clauses/clause.h> | |||
| #include <cpphibernate/modifier/where/clauses/equal.h> | |||
| #include <cpphibernate/modifier/where/clauses/not.h> | |||
| #include <cpphibernate/modifier/where/clauses/not.h> | |||
| #include <cpphibernate/modifier/where/clauses/or.h> | |||
| @@ -17,111 +17,143 @@ void field_t::print(std::ostream& os) const | |||
| { | |||
| os << indent << '{' | |||
| << incindent | |||
| << indent << "\"table_dataset_id\": " << table_dataset_id << "," | |||
| << indent << "\"value_dataset_id\": " << value_dataset_id << "," | |||
| << indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << "," | |||
| << indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << "," | |||
| << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
| << indent << "\"table_name\": \"" << table_name << "\"," | |||
| << indent << "\"field_name\": \"" << field_name << "\"," | |||
| << indent << "\"attributes\": " << misc::print_container(attributes, false) << "," | |||
| << indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << "," | |||
| << indent << "\"referenced_table\": " << (referenced_table ? std::string("\"") + referenced_table->table_name + "\"" : "null") | |||
| << decindent | |||
| << indent << '}'; | |||
| } | |||
| #define throw_not_implemented(p_ret, p_name, ...) \ | |||
| p_ret field_t::p_name(__VA_ARGS__) const \ | |||
| { \ | |||
| throw misc::hibernate_exception( \ | |||
| std::string("'") + table_name + "." + field_name + \ | |||
| "' does not implement the " #p_name "() method!"); \ | |||
| } | |||
| /* CRUD */ | |||
| << indent << "\"id\": " << id << "," | |||
| << indent << "\"dataset_id\": " << dataset_id << "," | |||
| << indent << "\"real_dataset_id\": " << real_dataset_id << "," | |||
| << indent << "\"value_id\": " << value_id << "," | |||
| << indent << "\"real_value_id\": " << real_value_id << "," | |||
| << indent << "\"value_is_nullable\": " << (value_is_nullable ? "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") << "," | |||
| << indent << "\"referenced_table\": " << (referenced_table ? std::string("\"") + referenced_table->table_name + "\"" : "null") << "," | |||
| throw_not_implemented(value_t, foreign_create_update, const create_update_context&) | |||
| << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
| << indent << "\"table_name\": \"" << table_name << "\"," | |||
| << indent << "\"field_name\": \"" << field_name << "\"," | |||
| << indent << "\"type\": \"" << type << "\"," | |||
| << indent << "\"create_arguments\": \"" << create_arguments << "\"," | |||
| /* properties */ | |||
| << indent << "\"convert_to_open\": \"" << convert_to_open << "\"," | |||
| << indent << "\"convert_to_close\": \"" << convert_to_close << "\"," | |||
| << indent << "\"convert_from_open\": \"" << convert_from_open << "\"," | |||
| << indent << "\"convert_from_close\": \"" << convert_from_close << "\"," | |||
| throw_not_implemented(bool, is_default, const data_context& context) | |||
| throw_not_implemented(string, type) | |||
| throw_not_implemented(string, create_table_arguments) | |||
| throw_not_implemented(string, generate_value, ::cppmariadb::connection&) | |||
| throw_not_implemented(value_t, get, const data_context& context) | |||
| throw_not_implemented(void, set, const data_context& context, const value_t&) | |||
| << indent << "\"attributes\": " << misc::print_container(attributes, false) | |||
| bool field_t::is_auto_generated() const | |||
| { return false; } | |||
| << decindent | |||
| << indent << '}'; | |||
| } | |||
| std::string field_t::convert_to_open() const | |||
| void field_t::update() | |||
| { | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
| id = 0; | |||
| dataset_id = 0; | |||
| real_dataset_id = 0; | |||
| value_id = 0; | |||
| real_value_id = 0; | |||
| value_is_nullable = false; | |||
| value_is_container = false; | |||
| value_is_auto_incremented = false; | |||
| table = nullptr; | |||
| referenced_table = nullptr; | |||
| type.clear(); | |||
| create_arguments.clear(); | |||
| /* conver_to_open */ | |||
| { | |||
| switch(*it) | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
| { | |||
| case attribute_t::hex: ss << "HEX("; break; | |||
| case attribute_t::compress: ss << "COMPRESS("; break; | |||
| case attribute_t::primary_key: break; | |||
| switch(*it) | |||
| { | |||
| case attribute_t::hex: | |||
| ss << "HEX("; | |||
| break; | |||
| case attribute_t::compress: | |||
| ss << "COMPRESS("; | |||
| break; | |||
| case attribute_t::primary_key: | |||
| ss << "UuidToBin("; | |||
| break; | |||
| } | |||
| } | |||
| convert_to_open = ss.str(); | |||
| } | |||
| return ss.str(); | |||
| } | |||
| std::string field_t::convert_to_close() const | |||
| { | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
| /* convert_to_close */ | |||
| { | |||
| switch(*it) | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.begin(); it != this->attributes.end(); ++it) | |||
| { | |||
| case attribute_t::hex: | |||
| case attribute_t::compress: | |||
| ss << ')'; | |||
| break; | |||
| case attribute_t::primary_key: | |||
| break; | |||
| switch(*it) | |||
| { | |||
| case attribute_t::hex: | |||
| case attribute_t::compress: | |||
| case attribute_t::primary_key: | |||
| ss << ')'; | |||
| break; | |||
| } | |||
| } | |||
| convert_to_close = ss.str(); | |||
| } | |||
| return ss.str(); | |||
| } | |||
| std::string field_t::convert_from_open() const | |||
| { | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
| /* convert_from_open */ | |||
| { | |||
| switch(*it) | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
| { | |||
| case attribute_t::hex: | |||
| ss << "UNHEX("; | |||
| break; | |||
| case attribute_t::compress: | |||
| ss << "UNCOMPRESS("; | |||
| break; | |||
| case attribute_t::primary_key: | |||
| break; | |||
| switch(*it) | |||
| { | |||
| case attribute_t::hex: | |||
| ss << "UNHEX("; | |||
| break; | |||
| case attribute_t::compress: | |||
| ss << "UNCOMPRESS("; | |||
| break; | |||
| case attribute_t::primary_key: | |||
| ss << "BinToUuid("; | |||
| break; | |||
| } | |||
| } | |||
| convert_from_open = ss.str(); | |||
| } | |||
| return ss.str(); | |||
| } | |||
| std::string field_t::convert_from_close() const | |||
| { | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
| /* convert_from_close */ | |||
| { | |||
| switch(*it) | |||
| std::ostringstream ss; | |||
| for (auto it = this->attributes.rbegin(); it != this->attributes.rend(); ++it) | |||
| { | |||
| case attribute_t::hex: | |||
| case attribute_t::compress: | |||
| ss << ')'; | |||
| break; | |||
| case attribute_t::primary_key: | |||
| break; | |||
| switch(*it) | |||
| { | |||
| case attribute_t::hex: | |||
| case attribute_t::compress: | |||
| case attribute_t::primary_key: | |||
| ss << ')'; | |||
| break; | |||
| } | |||
| } | |||
| convert_from_close = ss.str(); | |||
| } | |||
| return ss.str(); | |||
| } | |||
| } | |||
| #define throw_not_implemented(p_ret, p_name, ...) \ | |||
| p_ret field_t::p_name(__VA_ARGS__) const \ | |||
| { \ | |||
| throw misc::hibernate_exception( \ | |||
| std::string("'") + table_name + "." + field_name + \ | |||
| "' does not implement the " #p_name "() method!"); \ | |||
| } | |||
| /* CRUD */ | |||
| throw_not_implemented(value_t, foreign_create_update, const create_update_context&) | |||
| /* properties */ | |||
| throw_not_implemented(bool, is_default, const data_context& context) | |||
| throw_not_implemented(string, generate_value, ::cppmariadb::connection&) | |||
| throw_not_implemented(value_t, get, const data_context& context) | |||
| throw_not_implemented(void, set, const data_context& context, const value_t&) | |||
| @@ -13,7 +13,7 @@ using namespace ::cpphibernate::driver::mariadb_impl; | |||
| void schema_t::update() | |||
| { | |||
| // clear everything | |||
| // clear everything | |||
| for (auto& kvp : tables) | |||
| { | |||
| assert(static_cast<bool>(kvp.second)); | |||
| @@ -25,6 +25,13 @@ void schema_t::update() | |||
| table.foreign_table_one_fields.clear(); | |||
| table.foreign_table_many_fields.clear(); | |||
| table.data_fields.clear(); | |||
| for (auto& ptr : table.fields) | |||
| { | |||
| assert(ptr); | |||
| auto& field = *ptr; | |||
| field.update(); | |||
| } | |||
| } | |||
| // update references | |||
| @@ -54,12 +61,12 @@ void schema_t::update() | |||
| auto& field = *ptr; | |||
| // table | |||
| if (table.dataset_id != field.table_dataset_id) | |||
| if (table.dataset_id != field.dataset_id) | |||
| throw misc::hibernate_exception(std::string("dataset id of field '") + field.table_name + '.' + field.field_name + "' does not match!"); | |||
| field.table = &table; | |||
| // referenced table | |||
| it = tables.find(field.value_dataset_id); | |||
| it = tables.find(field.real_value_id); | |||
| auto referenced_table = (it != tables.end() | |||
| ? it->second.get() | |||
| : nullptr); | |||
| @@ -120,6 +127,23 @@ const table_t& schema_t::table(size_t dataset_id) const | |||
| return *it->second; | |||
| } | |||
| const field_t& schema_t::field(size_t field_id) const | |||
| { | |||
| for (auto& kvp : tables) | |||
| { | |||
| assert(kvp.second); | |||
| auto& table = *kvp.second; | |||
| for (auto& ptr : table.fields) | |||
| { | |||
| assert(ptr); | |||
| auto& field = *ptr; | |||
| if (field.id == field_id) | |||
| return field; | |||
| } | |||
| } | |||
| throw misc::hibernate_exception(std::string("unable to find field with id ") + std::to_string(field_id)); | |||
| } | |||
| #define exec_query() \ | |||
| do { \ | |||
| cpphibernate_debug_log("execute init query: " << ss.str()); \ | |||
| @@ -30,12 +30,12 @@ std::string build_init_stage1_query(const table_t& table) | |||
| { | |||
| assert(table.primary_key_field); | |||
| auto& key_info = *table.primary_key_field; | |||
| auto args = key_info.create_table_arguments(); | |||
| auto args = key_info.create_arguments; | |||
| os << indent | |||
| << "`" | |||
| << key_info.field_name | |||
| << "` " | |||
| << key_info.type() | |||
| << key_info.type | |||
| << " NOT NULL" | |||
| << (args.empty() ? "" : " ") | |||
| << args | |||
| @@ -52,7 +52,7 @@ std::string build_init_stage1_query(const table_t& table) | |||
| << "`" | |||
| << key_info.field_name | |||
| << "` " | |||
| << key_info.type() | |||
| << key_info.type | |||
| << " NOT NULL,"; | |||
| } | |||
| @@ -70,7 +70,7 @@ std::string build_init_stage1_query(const table_t& table) | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "` " | |||
| << ref_key_info.type() | |||
| << ref_key_info.type | |||
| << (field_info.value_is_nullable | |||
| ? " NULL DEFAULT NULL," | |||
| : " NOT NULL,"); | |||
| @@ -90,8 +90,17 @@ std::string build_init_stage1_query(const table_t& table) | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "` " | |||
| << ref_key_info.type() | |||
| << ref_key_info.type | |||
| << " NULL DEFAULT NULL,"; | |||
| if (field_info.value_is_ordered) | |||
| { | |||
| os << indent | |||
| << "`" | |||
| << field_info.table_name | |||
| << "_index_" | |||
| << field_info.field_name | |||
| << "` UNSIGNED INT NOT NULL,"; | |||
| } | |||
| } | |||
| /* data fields */ | |||
| @@ -103,7 +112,7 @@ std::string build_init_stage1_query(const table_t& table) | |||
| << "`" | |||
| << field_info.field_name | |||
| << "` " | |||
| << field_info.type() | |||
| << field_info.type | |||
| << (field_info.value_is_nullable | |||
| ? " NULL DEFAULT NULL," | |||
| : " NOT NULL,"); | |||
| @@ -353,18 +362,18 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| { | |||
| assert(table.primary_key_field); | |||
| auto& key_info = *table.primary_key_field; | |||
| if (!key_info.is_auto_generated()) | |||
| if (!key_info.value_is_auto_incremented) | |||
| { | |||
| if (index++) | |||
| os << ", "; | |||
| os << "`" | |||
| << key_info.field_name | |||
| << "`=" | |||
| << key_info.convert_to_open() | |||
| << key_info.convert_to_open | |||
| << "?" | |||
| << key_info.field_name | |||
| << "?" | |||
| << key_info.convert_to_close(); | |||
| << key_info.convert_to_close; | |||
| } | |||
| } | |||
| @@ -381,11 +390,11 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| os << "`" | |||
| << key_info.field_name | |||
| << "`=" | |||
| << key_info.convert_to_open() | |||
| << key_info.convert_to_open | |||
| << "?" | |||
| << key_info.field_name | |||
| << "?" | |||
| << key_info.convert_to_close(); | |||
| << key_info.convert_to_close; | |||
| } | |||
| /* foreign table one fields */ | |||
| @@ -405,13 +414,13 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "`=" | |||
| << key_info.convert_to_open() | |||
| << key_info.convert_to_open | |||
| << "?" | |||
| << key_info.table_name | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "?" | |||
| << key_info.convert_to_close(); | |||
| << key_info.convert_to_close; | |||
| } | |||
| /* foreign fields */ | |||
| @@ -431,13 +440,23 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "`=" | |||
| << key_info.convert_to_open() | |||
| << key_info.convert_to_open | |||
| << "?" | |||
| << field_info.table_name | |||
| << "_id_" | |||
| << field_info.field_name | |||
| << "?" | |||
| << key_info.convert_to_close(); | |||
| << key_info.convert_to_close; | |||
| if (field_info.value_is_ordered) | |||
| { | |||
| if (index++) | |||
| os << ", "; | |||
| os << "`" | |||
| << field_info.table_name | |||
| << "_index_" | |||
| << field_info.field_name | |||
| << "`=?\?"; | |||
| } | |||
| } | |||
| /* data fields */ | |||
| @@ -452,11 +471,11 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| os << "`" | |||
| << field_info.field_name | |||
| << "`=" | |||
| << field_info.convert_to_open() | |||
| << field_info.convert_to_open | |||
| << "?" | |||
| << field_info.field_name | |||
| << "?" | |||
| << field_info.convert_to_close(); | |||
| << field_info.convert_to_close; | |||
| } | |||
| /* type field for derived tables */ | |||
| @@ -476,11 +495,11 @@ std::string build_create_update_query(const table_t& table, const filter_t* filt | |||
| os << " WHERE `" | |||
| << key_info.field_name | |||
| << "`=" | |||
| << key_info.convert_to_open() | |||
| << key_info.convert_to_open | |||
| << "?" | |||
| << key_info.field_name | |||
| << "?" | |||
| << key_info.convert_to_close(); | |||
| << key_info.convert_to_close; | |||
| } | |||
| return os.str(); | |||
| @@ -503,7 +522,7 @@ std::string table_t::execute_create_update( | |||
| /* primary key */ | |||
| assert(primary_key_field); | |||
| if ( !primary_key_field->is_auto_generated() | |||
| if ( !primary_key_field->value_is_auto_incremented | |||
| && !is_update) | |||
| { | |||
| primary_key = primary_key_field->generate_value(context.connection); | |||
| @@ -550,15 +569,28 @@ std::string table_t::execute_create_update( | |||
| if (is_update && ptr != context.owner_field) | |||
| continue; | |||
| if ( context.owner_field | |||
| && ptr == context.owner_field) | |||
| auto& field_info = *ptr; | |||
| bool set_value = | |||
| context.owner_field | |||
| && ptr == context.owner_field; | |||
| if (set_value) | |||
| { | |||
| assert(!context.owner_key.empty()); | |||
| statement.set(index, context.owner_key); | |||
| } | |||
| else | |||
| { | |||
| statement.set_null(index); | |||
| } | |||
| ++index; | |||
| if (field_info.value_is_ordered) | |||
| { | |||
| if (set_value) statement.set(index, context.index); | |||
| else statement.set(index, 0); | |||
| ++index; | |||
| } | |||
| } | |||
| /* data fields */ | |||
| @@ -604,7 +636,7 @@ std::string table_t::execute_create_update( | |||
| cpphibernate_debug_log("execute UPDATE query: " << statement.query(connection)); | |||
| } | |||
| if ( primary_key_field->is_auto_generated() | |||
| if ( primary_key_field->value_is_auto_incremented | |||
| && !is_update) | |||
| { | |||
| auto id = connection.execute_id(statement); | |||
| @@ -12,6 +12,7 @@ Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX | |||
| Project ( test_cpphibernate ) | |||
| File ( GLOB_RECURSE SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
| List ( FILTER SOURCE_FILES EXCLUDE REGEX "/_[A-Za-z0-9_-]*\.cpp$" ) | |||
| Add_Executable ( test_cpphibernate EXCLUDE_FROM_ALL ${SOURCE_FILES} ) | |||
| Target_Link_Libraries ( | |||
| test_cpphibernate | |||
| @@ -103,7 +103,9 @@ TEST(CppHibernateTests, create_test3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_list`='X0X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_vector`='X0X', " | |||
| "`u32_data`='X5X', " | |||
| "`i32_data`='X6X', " | |||
| "`u64_data`='X7X', " | |||
| @@ -325,7 +327,9 @@ TEST(CppHibernateTests, create_derived3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d1289f0-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_index_test3_list`='X0X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_vector`='X0X', " | |||
| "`u32_data`='X100X', " | |||
| "`i32_data`='X101X', " | |||
| "`u64_data`='X102X', " | |||
| @@ -340,7 +344,9 @@ TEST(CppHibernateTests, create_derived3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d128b26-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_index_test3_list`='X1X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_vector`='X0X', " | |||
| "`u32_data`='X110X', " | |||
| "`i32_data`='X111X', " | |||
| "`u64_data`='X112X', " | |||
| @@ -355,11 +361,13 @@ TEST(CppHibernateTests, create_derived3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d128eb4-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_list`='X0X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`u32_data`='X120X', " | |||
| "`i32_data`='X121X', " | |||
| "`u64_data`='X122X', " | |||
| "`i64_data`='X123X'", | |||
| "`tbl_derived3_index_test3_vector`='X0X', " | |||
| "`u32_data`='X200X', " | |||
| "`i32_data`='X201X', " | |||
| "`u64_data`='X202X', " | |||
| "`i64_data`='X203X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||
| @@ -370,11 +378,13 @@ TEST(CppHibernateTests, create_derived3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d128ffe-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_list`='X0X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`u32_data`='X130X', " | |||
| "`i32_data`='X131X', " | |||
| "`u64_data`='X132X', " | |||
| "`i64_data`='X133X'", | |||
| "`tbl_derived3_index_test3_vector`='X1X', " | |||
| "`u32_data`='X210X', " | |||
| "`i32_data`='X211X', " | |||
| "`u64_data`='X212X', " | |||
| "`i64_data`='X213X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "SELECT Uuid()", result_used({ | |||
| @@ -385,11 +395,13 @@ TEST(CppHibernateTests, create_derived3) | |||
| "SET " | |||
| "`tbl_test3_id`=UuidToBin('X3d129134-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`tbl_derived3_id_test3_list`=UuidToBin(null), " | |||
| "`tbl_derived3_index_test3_list`='X0X', " | |||
| "`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X'), " | |||
| "`u32_data`='X140X', " | |||
| "`i32_data`='X141X', " | |||
| "`u64_data`='X142X', " | |||
| "`i64_data`='X143X'", | |||
| "`tbl_derived3_index_test3_vector`='X2X', " | |||
| "`u32_data`='X220X', " | |||
| "`i32_data`='X221X', " | |||
| "`u64_data`='X222X', " | |||
| "`i64_data`='X223X'", | |||
| result_affected_rows(1)); | |||
| expect_query(mock, "COMMIT"); | |||
| @@ -418,20 +430,20 @@ TEST(CppHibernateTests, create_derived3) | |||
| d3.test3_list.back().u64_data = 112; | |||
| d3.test3_list.back().i64_data = 113; | |||
| d3.test3_vector.emplace_back(); | |||
| d3.test3_vector.back().u32_data = 120; | |||
| d3.test3_vector.back().i32_data = 121; | |||
| d3.test3_vector.back().u64_data = 122; | |||
| d3.test3_vector.back().i64_data = 123; | |||
| d3.test3_vector.back().u32_data = 200; | |||
| d3.test3_vector.back().i32_data = 201; | |||
| d3.test3_vector.back().u64_data = 202; | |||
| d3.test3_vector.back().i64_data = 203; | |||
| d3.test3_vector.emplace_back(); | |||
| d3.test3_vector.back().u32_data = 130; | |||
| d3.test3_vector.back().i32_data = 131; | |||
| d3.test3_vector.back().u64_data = 132; | |||
| d3.test3_vector.back().i64_data = 133; | |||
| d3.test3_vector.back().u32_data = 210; | |||
| d3.test3_vector.back().i32_data = 211; | |||
| d3.test3_vector.back().u64_data = 212; | |||
| d3.test3_vector.back().i64_data = 213; | |||
| d3.test3_vector.emplace_back(); | |||
| d3.test3_vector.back().u32_data = 140; | |||
| d3.test3_vector.back().i32_data = 141; | |||
| d3.test3_vector.back().u64_data = 142; | |||
| d3.test3_vector.back().i64_data = 143; | |||
| d3.test3_vector.back().u32_data = 220; | |||
| d3.test3_vector.back().i32_data = 221; | |||
| d3.test3_vector.back().u64_data = 222; | |||
| d3.test3_vector.back().i64_data = 223; | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| @@ -79,7 +79,9 @@ TEST(CppHibernateTests, init) | |||
| "(\n" | |||
| " `tbl_test3_id` BINARY(16) NOT NULL,\n" | |||
| " `tbl_derived3_id_test3_list` BINARY(16) NULL DEFAULT NULL,\n" | |||
| " `tbl_derived3_index_test3_list` UNSIGNED INT NOT NULL,\n" | |||
| " `tbl_derived3_id_test3_vector` BINARY(16) NULL DEFAULT NULL,\n" | |||
| " `tbl_derived3_index_test3_vector` UNSIGNED INT NOT NULL,\n" | |||
| " `u32_data` INT UNSIGNED NOT NULL,\n" | |||
| " `i32_data` INT NOT NULL,\n" | |||
| " `u64_data` BIGINT UNSIGNED NOT NULL,\n" | |||
| @@ -0,0 +1,34 @@ | |||
| #include <cpphibernate/driver/mariadb.h> | |||
| #include "test_helper.h" | |||
| #include "test_schema.h" | |||
| #include "mariadb_mock.h" | |||
| using namespace ::testing; | |||
| using namespace ::cpphibernate; | |||
| TEST(CppHibernateTests, read_test1) | |||
| { | |||
| StrictMock<mariadb_mock> mock; | |||
| // expect_query(mock, "START TRANSACTION"); | |||
| // expect_query(mock, "COMMIT"); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| EXPECT_CALL( | |||
| mock, | |||
| mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _)) | |||
| .Times(AnyNumber()) | |||
| .WillRepeatedly(WithArgs<1, 2, 3>(EscapeString())); | |||
| test1 t1; | |||
| t1.id = uuid("1e133ad8-ad2e-11e8-98d0-529269fb1459"); | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<driver::mariadb>(test_schema, connection); | |||
| context.read(t1); | |||
| } | |||