| @@ -0,0 +1,34 @@ | |||
| #pragma once | |||
| #include <cppmp.h> | |||
| #include <boost/hana.hpp> | |||
| namespace cpphibernate | |||
| { | |||
| namespace mp = ::cppmp; | |||
| namespace hana = ::boost::hana; | |||
| } | |||
| #cmakedefine CPPHIBERNATE_DEBUG | |||
| #cmakedefine CPPHIBERNATE_HAS_CPPLOGGING | |||
| #cmakedefine CPPHIBERNATE_HAS_CPPMARIADB | |||
| #ifdef CPPHIBERNATE_HAS_CPPLOGGING | |||
| #include <cpplogging/interface.h> | |||
| #define cpphibernate_log(p_level) \ | |||
| cpplogging_global_log(p_level) | |||
| #else | |||
| #include <iostream> | |||
| #define cpphibernate_log(p_level) \ | |||
| ::std::cout << #p_level << ' ' << __FILE__ << ':' << __LINE__ << " - " | |||
| #endif | |||
| #ifdef CPPHIBERNATE_DEBUG | |||
| #define cpphibernate_log_debug(...) \ | |||
| cpphibernate_log(debug) << __VA_ARGS__ | |||
| #else | |||
| #define cpphibernate_log_debug(...) \ | |||
| do { } while (0) | |||
| #endif | |||
| @@ -1,3 +1,6 @@ | |||
| Option ( CPPHIBERNATE_USE_CPPLOGGING | |||
| "Try to find the cpplogging library and use it as logger instead of logging to std::cout." | |||
| OFF ) | |||
| Option ( CPPHIBERNATE_INSTALL_HEADER | |||
| "Install headers of cpphibernate." | |||
| ON ) | |||
| @@ -13,3 +16,6 @@ Option ( CPPHIBERNATE_INSTALL_DEBUG | |||
| Option ( CPPHIBERNATE_NO_STRIP | |||
| "Do not strip debug symbols from binary." | |||
| OFF ) | |||
| Option ( CPPHIBERNATE_DEBUG | |||
| "Write extra debug output to the console/logger." | |||
| OFF ) | |||
| @@ -1,12 +0,0 @@ | |||
| #pragma once | |||
| #include <cppmp.h> | |||
| #include <boost/hana.hpp> | |||
| namespace cpphibernate | |||
| { | |||
| namespace mp = ::cppmp; | |||
| namespace hana = ::boost::hana; | |||
| } | |||
| @@ -18,15 +18,17 @@ namespace cpphibernate | |||
| { static_assert(sizeof...(T_args) == -1, "Invalid parameters for context::init(...)!"); } | |||
| }; | |||
| constexpr decltype(auto) init = mp::generic_predicate<init_builder> { }; | |||
| #if 0 | |||
| template<typename T_impl> | |||
| struct init_builder<mp::list<T_impl, bool>, void> | |||
| template<typename T_impl, typename T_bool> | |||
| struct init_builder< | |||
| mp::list<T_impl, T_bool>, | |||
| mp::enable_if_t<mp::is_same_v<bool, mp::decay_t<T_bool>>>> | |||
| { | |||
| static constexpr decltype(auto) apply(T_impl& impl, bool recreate) | |||
| { return impl.init(recreate); } | |||
| }; | |||
| #endif | |||
| constexpr decltype(auto) init = mp::generic_predicate<init_builder> { }; | |||
| /* create_builder */ | |||
| template<typename X, typename = void> | |||
| @@ -120,27 +122,27 @@ namespace cpphibernate | |||
| template<typename T_driver, typename T_schema> | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) context<T_driver, T_schema>::init(T_args&&... args) | |||
| { return __impl::init(*this, std::forward<T_args>(args)...); } | |||
| { return __impl::init(this->impl(), std::forward<T_args>(args)...); } | |||
| template<typename T_driver, typename T_schema> | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) context<T_driver, T_schema>::create(T_args&&... args) | |||
| { return __impl::create(*this, std::forward<T_args>(args)...); } | |||
| { return __impl::create(this->impl(), std::forward<T_args>(args)...); } | |||
| template<typename T_driver, typename T_schema> | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) context<T_driver, T_schema>::read(T_args&&... args) | |||
| { return __impl::read(*this, std::forward<T_args>(args)...); } | |||
| { return __impl::read(this->impl(), std::forward<T_args>(args)...); } | |||
| template<typename T_driver, typename T_schema> | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) context<T_driver, T_schema>::update(T_args&&... args) | |||
| { return __impl::update(*this, std::forward<T_args>(args)...); } | |||
| { return __impl::update(this->impl(), std::forward<T_args>(args)...); } | |||
| template<typename T_driver, typename T_schema> | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) context<T_driver, T_schema>::destroy(T_args&&... args) | |||
| { return __impl::destroy(*this, std::forward<T_args>(args)...); } | |||
| { return __impl::destroy(this->impl(), std::forward<T_args>(args)...); } | |||
| #if 0 | |||
| @@ -1,8 +1,14 @@ | |||
| #pragma once | |||
| #include <cpphibernate/driver/mariadb/driver.h> | |||
| #include <cpphibernate/config.h> | |||
| namespace cpphibernate | |||
| { | |||
| using mariadb_driver = ::cpphibernate::mariadb::driver_t; | |||
| } | |||
| #ifdef CPPHIBERNATE_HAS_CPPMARIADB | |||
| #include <cpphibernate/driver/mariadb/driver.h> | |||
| namespace cpphibernate | |||
| { | |||
| using mariadb_driver = ::cpphibernate::mariadb::driver_t; | |||
| } | |||
| #else | |||
| #error "cppmariadb library was not found!" | |||
| #endif | |||
| @@ -6,6 +6,13 @@ | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| enum init_stage | |||
| { | |||
| unknown = 0, | |||
| stage1, | |||
| stage2, | |||
| }; | |||
| enum class attribute_t; | |||
| struct attributes_t; | |||
| struct field_t; | |||
| @@ -4,6 +4,7 @@ | |||
| #include "../forward.h" | |||
| #include "../tables/tables.h" | |||
| #include "../../context.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| @@ -52,6 +53,11 @@ namespace mariadb { | |||
| */ | |||
| std::ostream& print(std::ostream& os) const; | |||
| /** | |||
| * @brief Initialize the whole schema using the passed context. | |||
| */ | |||
| void init(const init_context& context) const; | |||
| private: | |||
| /** | |||
| * @brief Initialize the field. | |||
| @@ -6,6 +6,7 @@ | |||
| #include "../forward.h" | |||
| #include "../fields/fields.h" | |||
| #include "../../context.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| @@ -87,11 +88,38 @@ namespace mariadb { | |||
| */ | |||
| std::ostream& print(std::ostream& os) const; | |||
| /** | |||
| * @brief Initialize the table using the passed context. | |||
| * | |||
| * The initialization is splitted into two stages. In the first stage the | |||
| * table is created. In the second stage the table contraints are added. | |||
| * The first stage must be completed for all tables before stage two of | |||
| * any table is executed. | |||
| */ | |||
| void init(const init_context& context, init_stage stage) const; | |||
| private: | |||
| /** | |||
| * @brief Initialize the field. | |||
| */ | |||
| void init(); | |||
| private: | |||
| using statement_ptr_u = std::unique_ptr<::cppmariadb::statement>; | |||
| mutable statement_ptr_u _statement_init_stage1; //!< Statement for init stage 1 (create table). | |||
| mutable statement_ptr_u _statement_init_stage2; //!< Statement for init stage 2 (alter table). | |||
| /** | |||
| * @brief Get or create the mariadb statement for init stage 1. | |||
| */ | |||
| ::cppmariadb::statement& get_statement_init_stage1() const; | |||
| /** | |||
| * @brief Get or create the mariadb statement for init stage 2. | |||
| */ | |||
| ::cppmariadb::statement* get_statement_init_stage2() const; | |||
| }; | |||
| namespace __impl | |||
| @@ -0,0 +1,4 @@ | |||
| #pragma once | |||
| #include "context/base_context.h" | |||
| #include "context/init_context.h" | |||
| @@ -0,0 +1,29 @@ | |||
| #pragma once | |||
| #include <cppmariadb.h> | |||
| #include <cpphibernate/config.h> | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| struct schema_t; | |||
| /** | |||
| * @brief Base class for all mariadb driver context classes. | |||
| */ | |||
| struct base_context | |||
| { | |||
| const schema_t& schema; //!< schema to use for the operation | |||
| ::cppmariadb::connection& connection; //!< mariadb connection to use for executing queries | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| inline base_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection); | |||
| }; | |||
| } } | |||
| #include "base_context.inl" | |||
| @@ -0,0 +1,17 @@ | |||
| #pragma once | |||
| #include "base_context.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /* base_context */ | |||
| base_context::base_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection) | |||
| : schema (p_schema) | |||
| , connection(p_connection) | |||
| { } | |||
| } } | |||
| @@ -0,0 +1,26 @@ | |||
| #pragma once | |||
| #include <cppmariadb.h> | |||
| #include <cpphibernate/config.h> | |||
| #include "base_context.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /** | |||
| * @brief Mariadb driver context for initializing the database. | |||
| */ | |||
| struct init_context | |||
| : public base_context | |||
| { | |||
| bool recreate; | |||
| inline init_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection, | |||
| bool p_recreate); | |||
| }; | |||
| } } | |||
| #include "init_context.inl" | |||
| @@ -0,0 +1,18 @@ | |||
| #pragma once | |||
| #include "init_context.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /* init_context */ | |||
| init_context::init_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection, | |||
| bool p_recreate) | |||
| : base_context (p_schema, p_connection) | |||
| , recreate (p_recreate) | |||
| { } | |||
| } } | |||
| @@ -3,21 +3,22 @@ | |||
| #include <cppmariadb.h> | |||
| #include "classes.h" | |||
| // #include <cpphibernate/config.h> | |||
| // #include <cpphibernate/driver/mariadb/impl.h> | |||
| // #include <cpphibernate/driver/mariadb/schema.h> | |||
| // #include <cpphibernate/driver/mariadb/schema/filter.h> | |||
| #include "impl/driver_impl.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /** | |||
| * @brief Mariadb driver. | |||
| * | |||
| * The normal cpphibernate context will be inherited from this class. So all methods | |||
| * defined here will be part of the context class. | |||
| */ | |||
| struct driver_t | |||
| { | |||
| private: | |||
| schema_ptr_u _schema; | |||
| driver_impl_t _impl; //!< Driver implementation. | |||
| ::cppmariadb::connection * _connection; //!< Mariadb connection to use for queries. | |||
| public: | |||
| /** | |||
| @@ -31,6 +32,23 @@ namespace mariadb { | |||
| */ | |||
| inline std::ostream& print(std::ostream& os) const; | |||
| /** | |||
| * @brief Get the connection currently assigned to the driver. | |||
| */ | |||
| inline ::cppmariadb::connection * connection() const; | |||
| /** | |||
| * @brief Set the connection to use for queries. | |||
| * This will invalidate all cached queries and strings. | |||
| */ | |||
| inline void connection(::cppmariadb::connection * p_connection); | |||
| protected: | |||
| /** | |||
| * @brief Get the imlementation object of the driver. | |||
| */ | |||
| inline auto& impl() const; | |||
| /* | |||
| public: | |||
| using lock_type = std::unique_ptr<transaction_lock>; | |||
| @@ -1,16 +1,26 @@ | |||
| #pragma once | |||
| #include "driver.h" | |||
| #include "impl/driver_impl.inl" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| template<typename T_schema> | |||
| driver_t::driver_t(T_schema&& p_schema) | |||
| : _schema(make_schema(std::forward<T_schema>(p_schema))) | |||
| : _impl(*this, std::forward<T_schema>(p_schema)) | |||
| { } | |||
| std::ostream& driver_t::print(std::ostream& os) const | |||
| { return _schema->print(os); } | |||
| { return _impl.schema->print(os); } | |||
| ::cppmariadb::connection* driver_t::connection() const | |||
| { return _connection; } | |||
| void driver_t::connection(::cppmariadb::connection * p_connection) | |||
| { _connection = p_connection; } | |||
| auto& driver_t::impl() const | |||
| { return _impl; } | |||
| } } | |||
| @@ -1,7 +1,7 @@ | |||
| #pragma once | |||
| #include "helper/key_properties.h" | |||
| #include "helper/type_properties.h" | |||
| #include "helper/key_properties.h" | |||
| #include "helper/nullable_helper.h" | |||
| #include "helper/transaction_lock.h" | |||
| #include "helper/type_properties.h" | |||
| @@ -1,93 +0,0 @@ | |||
| #pragma once | |||
| #include <memory> | |||
| #include <iomanip> | |||
| #include <cppmariadb.h> | |||
| #include <cpphibernate/config.h> | |||
| #include <cpputils/misc/type_helper.h> | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| struct transaction_lock final | |||
| { | |||
| public: | |||
| inline transaction_lock(::cppmariadb::connection& con) | |||
| { begin_transaction(*this, con); } | |||
| inline ~transaction_lock() | |||
| { close_transaction(*this); } | |||
| inline bool commit() | |||
| { return commit_transaction(*this); } | |||
| private: | |||
| using transaction_ptr_type = std::unique_ptr<::cppmariadb::transaction>; | |||
| #ifdef CPPHIBERNATE_DEBUG | |||
| # define debug_log(str) cpphibernate_debug_log( \ | |||
| "transaction (id=" << std::setw(8) << std::setfill(' ') << lock.id << \ | |||
| ", counter=" << std::setw(2) << std::setfill(' ') << counter << ") " str) | |||
| struct counter { }; | |||
| size_t id { utl::unique_counter<counter>::next() }; | |||
| #else | |||
| # define debug_log(str) do { } while(0) | |||
| #endif | |||
| static size_t& ref_counter() | |||
| { | |||
| static size_t value = 0; | |||
| return value; | |||
| } | |||
| static transaction_ptr_type& ref_transaction_ptr() | |||
| { | |||
| static transaction_ptr_type value; | |||
| return value; | |||
| } | |||
| static void begin_transaction(const transaction_lock& lock, ::cppmariadb::connection& con) | |||
| { | |||
| auto& counter = ref_counter(); | |||
| ++counter; | |||
| debug_log("+++"); | |||
| if (counter == 1) | |||
| { | |||
| debug_log("begin"); | |||
| ref_transaction_ptr().reset(new ::cppmariadb::transaction(con)); | |||
| } | |||
| } | |||
| static bool commit_transaction(const transaction_lock& lock) | |||
| { | |||
| auto& counter = ref_counter(); | |||
| if (counter == 1) | |||
| { | |||
| debug_log("commit"); | |||
| ref_transaction_ptr()->commit(); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| static void close_transaction(const transaction_lock& lock) | |||
| { | |||
| auto& counter = ref_counter(); | |||
| debug_log("---"); | |||
| if (counter <= 1) | |||
| { | |||
| debug_log("close"); | |||
| counter = 0; | |||
| ref_transaction_ptr().reset(); | |||
| } | |||
| else | |||
| { | |||
| --counter; | |||
| } | |||
| } | |||
| }; | |||
| #undef debug_log | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| @@ -1,32 +1,43 @@ | |||
| #pragma once | |||
| #include <cppmariadb.h> | |||
| #include <cpphibernate/misc.h> | |||
| #include <cpphibernate/config.h> | |||
| #include <cpphibernate/driver/mariadb/schema/field.fwd.h> | |||
| #include <cpphibernate/driver/mariadb/schema/table.fwd.h> | |||
| #include <cpphibernate/driver/mariadb/schema/filter.fwd.h> | |||
| #include <cpphibernate/driver/mariadb/schema/schema.fwd.h> | |||
| #include "../classes.h" | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| /* base_context */ | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /** | |||
| * @brief Base class for all mariadb driver context classes. | |||
| */ | |||
| struct base_context | |||
| { | |||
| const schema_t& schema; | |||
| ::cppmariadb::connection& connection; | |||
| const schema_t& schema; //!< schema to use for the operation | |||
| ::cppmariadb::connection& connection; //!< mariadb connection to use for executing queries | |||
| /** | |||
| * @brief Constructor. | |||
| */ | |||
| inline base_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection) | |||
| : schema (p_schema) | |||
| , connection(p_connection) | |||
| { } | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection); | |||
| }; | |||
| /** | |||
| * @brief Mariadb driver context for initializing the database. | |||
| */ | |||
| struct init_context | |||
| : public base_context | |||
| { | |||
| bool recreate; | |||
| inline init_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection, | |||
| bool p_recreate); | |||
| }; | |||
| #if 0 | |||
| /* init_context */ | |||
| struct init_context | |||
| @@ -213,6 +224,5 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| p_connection) | |||
| { } | |||
| }; | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| #endif | |||
| } } | |||
| @@ -1,10 +1,22 @@ | |||
| #pragma once | |||
| #include <cpphibernate/driver/mariadb/helper/context.h> | |||
| #include "context.h" | |||
| beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /* base_context */ | |||
| base_context::base_context( | |||
| const schema_t& p_schema, | |||
| ::cppmariadb::connection& p_connection) | |||
| : schema (p_schema) | |||
| , connection(p_connection) | |||
| { } | |||
| #if 0 | |||
| /* data_context */ | |||
| template<typename T_dataset> | |||
| @@ -86,6 +98,5 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| ptr.release(); | |||
| return *static_cast<dataset_type*>(data); | |||
| } | |||
| } | |||
| end_namespace_cpphibernate_driver_mariadb | |||
| #endif | |||
| } } | |||
| @@ -0,0 +1,99 @@ | |||
| #pragma once | |||
| #include <cppmariadb.h> | |||
| #include "../classes.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| struct driver_t; | |||
| /** | |||
| * @brief Actual implementation of the mariadb driver. | |||
| * | |||
| * This class is used/owner by the mariadb driver class (as a member). | |||
| * The public interface of this class will not be part of the cpphibernate context. | |||
| */ | |||
| struct driver_impl_t | |||
| { | |||
| driver_t& owner; | |||
| schema_ptr_u schema; | |||
| /** | |||
| * @brief Constructor. | |||
| * @param[in] p_owner Object the driver implementation is owned by. | |||
| * @param[in] p_schema Cpphibernate schema to use with the driver. | |||
| */ | |||
| template<typename T_schema> | |||
| inline driver_impl_t(driver_t& p_owner, T_schema&& p_schema); | |||
| /** | |||
| * @brief Initialize the schema in the database. | |||
| * | |||
| * @param[in] recreate Recreate the whole schema (this will drop all existing data). | |||
| */ | |||
| inline void init(bool recreate) const; | |||
| /* | |||
| } | |||
| protected: | |||
| inline void init_impl(bool recreate) const | |||
| { | |||
| transaction_lock trans(*_connection); | |||
| _schema.init(init_context(_schema, *_connection, recreate)); | |||
| trans.commit(); | |||
| } | |||
| template<typename T_dataset> | |||
| inline void create_impl(T_dataset& dataset) const | |||
| { | |||
| 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 | |||
| { | |||
| using dataset_type = mp::decay_t<T_dataset>; | |||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
| auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
| auto& table = _schema.table(dataset_id); | |||
| auto context = make_read_context(dataset, _schema, *_connection, _filter); | |||
| context.where = build_where(_schema, modifiers).query(*_connection); | |||
| context.limit = build_limit(modifiers).query(*_connection); | |||
| context.order_by = build_order_by(_schema, modifiers).query(*_connection); | |||
| transaction_lock trans(*_connection); | |||
| table.read(context); | |||
| trans.commit(); | |||
| } | |||
| template<typename T_dataset> | |||
| inline void update_impl(T_dataset& dataset) const | |||
| { | |||
| create_update_impl_t<T_dataset>::apply( | |||
| create_update_context(dataset, _schema, *_connection, _filter, true)); | |||
| } | |||
| template<typename T_dataset> | |||
| inline void destroy_impl(T_dataset& dataset) const | |||
| { | |||
| using dataset_type = mp::decay_t<T_dataset>; | |||
| using real_dataset_type = misc::real_dataset_t<dataset_type>; | |||
| auto dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>); | |||
| auto& table = _schema.table(dataset_id); | |||
| destroy_context context(dataset, _schema, *_connection); | |||
| context.where = table.get_where_primary_key(context); | |||
| destroy_impl_t<T_dataset>::apply(context); | |||
| } | |||
| */ | |||
| }; | |||
| } } | |||
| @@ -0,0 +1,27 @@ | |||
| #pragma once | |||
| #include "driver_impl.h" | |||
| #include "../driver.h" | |||
| namespace cpphibernate { | |||
| namespace mariadb { | |||
| /* driver_impl_t */ | |||
| template<typename T_schema> | |||
| driver_impl_t::driver_impl_t(driver_t& p_owner, T_schema&& p_schema) | |||
| : owner (p_owner) | |||
| , schema(make_schema(std::forward<T_schema>(p_schema))) | |||
| { } | |||
| void driver_impl_t::init(bool recreate) const | |||
| { | |||
| auto * connection = owner.connection(); | |||
| if (!connection) | |||
| throw exception("Cpphibernate mariadb driver is not connected to any database!"); | |||
| ::cppmariadb::transaction_lock trans(*connection); | |||
| schema->init(init_context(*schema, *connection, recreate)); | |||
| trans.commit(); | |||
| } | |||
| } } | |||
| @@ -6,6 +6,7 @@ | |||
| #include <vector> | |||
| #include <memory> | |||
| #include <cppcore/misc/type_helper.h> | |||
| #include <cppcore/misc/nullable.h> | |||
| #include <cpphibernate/config.h> | |||
| @@ -11,11 +11,20 @@ Find_Package ( cppmariadb REQUIRED ) | |||
| If ( cppmariadb_FOUND ) | |||
| Set ( CPPHIBERNATE_HAS_CPPMARIADB true ) | |||
| EndIf ( ) | |||
| If ( CPPHIBERNATE_USE_CPPLOGGING ) | |||
| Find_Package ( cpplogging ) | |||
| If ( cpplogging_FOUND ) | |||
| Set ( CPPHIBERNATE_HAS_CPPLOGGING true ) | |||
| EndIf ( ) | |||
| EndIf ( ) | |||
| # Object Library ################################################################################## | |||
| Set ( CMAKE_POSITION_INDEPENDENT_CODE ON ) | |||
| Set ( CPPHIBERNATE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../include ) | |||
| Set ( CPPHIBERNATE_GENERATED_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated/include ) | |||
| Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/../cmake/config.h.in | |||
| ${CPPHIBERNATE_GENERATED_INCLUDE_DIR}/cpphibernate/config.h ) | |||
| File ( GLOB_RECURSE CPPHIBERNATE_HEADER_FILES ${CPPHIBERNATE_INCLUDE_DIR}/*.h ) | |||
| File ( GLOB_RECURSE CPPHIBERNATE_INLINE_FILES ${CPPHIBERNATE_INCLUDE_DIR}/*.inl ) | |||
| File ( GLOB_RECURSE CPPHIBERNATE_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp ) | |||
| @@ -27,6 +36,7 @@ Add_Library ( cpphibernate-objects | |||
| Target_Include_Directories ( cpphibernate-objects | |||
| PUBLIC | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_INCLUDE_DIR}> | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_GENERATED_INCLUDE_DIR}> | |||
| $<INSTALL_INTERFACE:${CPPHIBERNATE_INSTALL_DIR_INCLUDE}> ) | |||
| Target_Link_Libraries ( cpphibernate-objects | |||
| PUBLIC | |||
| @@ -38,6 +48,11 @@ If ( CPPHIBERNATE_HAS_CPPMARIADB ) | |||
| PUBLIC | |||
| cppmariadb::cppmariadb-shared ) | |||
| EndIf ( ) | |||
| If ( CPPHIBERNATE_HAS_CPPLOGGING ) | |||
| Target_Link_Libraries ( cpphibernate-objects | |||
| PUBLIC | |||
| cpplogging::cpplogging-shared ) | |||
| EndIf ( ) | |||
| # Static Library ################################################################################## | |||
| @@ -49,12 +64,18 @@ Set_Target_Properties ( cpphibernate-static | |||
| Target_Include_Directories ( cpphibernate-static | |||
| PUBLIC | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_INCLUDE_DIR}> | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_GENERATED_INCLUDE_DIR}> | |||
| $<INSTALL_INTERFACE:${CPPHIBERNATE_INSTALL_DIR_INCLUDE}> ) | |||
| If ( CPPHIBERNATE_HAS_CPPMARIADB ) | |||
| Target_Link_Libraries ( cpphibernate-static | |||
| PUBLIC | |||
| cppmariadb::cppmariadb-static ) | |||
| EndIf ( ) | |||
| If ( CPPHIBERNATE_HAS_CPPLOGGING ) | |||
| Target_Link_Libraries ( cpphibernate-objects | |||
| PUBLIC | |||
| cpplogging::cpplogging-static ) | |||
| EndIf ( ) | |||
| # Shared Library ################################################################################## | |||
| @@ -67,12 +88,18 @@ Set_Target_Properties ( cpphibernate-shared | |||
| Target_Include_Directories ( cpphibernate-shared | |||
| PUBLIC | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_INCLUDE_DIR}> | |||
| $<BUILD_INTERFACE:${CPPHIBERNATE_GENERATED_INCLUDE_DIR}> | |||
| $<INSTALL_INTERFACE:${CPPHIBERNATE_INSTALL_DIR_INCLUDE}> ) | |||
| If ( CPPHIBERNATE_HAS_CPPMARIADB ) | |||
| Target_Link_Libraries ( cpphibernate-shared | |||
| PUBLIC | |||
| cppmariadb::cppmariadb-shared ) | |||
| EndIf ( ) | |||
| If ( CPPHIBERNATE_HAS_CPPLOGGING ) | |||
| Target_Link_Libraries ( cpphibernate-objects | |||
| PUBLIC | |||
| cpplogging::cpplogging-shared ) | |||
| EndIf ( ) | |||
| # Optimization #################################################################################### | |||
| @@ -0,0 +1,89 @@ | |||
| #include <cpphibernate/driver/mariadb/classes/schema/schema.h> | |||
| using namespace ::cpphibernate; | |||
| using namespace ::cpphibernate::mariadb; | |||
| #define exec_query() \ | |||
| do { \ | |||
| connection.execute(ss.str()); \ | |||
| ss.str(std::string()); \ | |||
| ss.clear(); \ | |||
| } while(0) | |||
| void schema_t::init(const init_context& context) const | |||
| { | |||
| std::ostringstream ss; | |||
| auto& connection = context.connection; | |||
| if (context.recreate) | |||
| { | |||
| ss << "DROP DATABASE IF EXISTS `" | |||
| << name | |||
| << "`"; | |||
| exec_query(); | |||
| } | |||
| /* create schema */ | |||
| ss << "CREATE SCHEMA IF NOT EXISTS `" | |||
| << name | |||
| << "` DEFAULT CHARACTER SET utf8"; | |||
| exec_query(); | |||
| /* use schema */ | |||
| ss << "USE `" | |||
| << name | |||
| << "`"; | |||
| exec_query(); | |||
| /* UuidToBin */ | |||
| ss << "CREATE FUNCTION IF NOT EXISTS UuidToBin(_uuid CHAR(36))\n" | |||
| " RETURNS BINARY(16)\n" | |||
| " LANGUAGE SQL\n" | |||
| " DETERMINISTIC\n" | |||
| " CONTAINS SQL\n" | |||
| " SQL SECURITY INVOKER\n" | |||
| "RETURN\n" | |||
| " UNHEX(CONCAT(\n" | |||
| " SUBSTR(_uuid, 25, 12),\n" // node id | |||
| " SUBSTR(_uuid, 20, 4),\n" // clock sequence | |||
| " SUBSTR(_uuid, 15, 4),\n" // time high and version | |||
| " SUBSTR(_uuid, 10, 4),\n" // time mid | |||
| " SUBSTR(_uuid, 1, 8)\n" // time low | |||
| " )\n" | |||
| ")"; | |||
| exec_query(); | |||
| /* BinToUuid */ | |||
| ss << "CREATE FUNCTION IF NOT EXISTS BinToUuid(_bin BINARY(16))\n" | |||
| " RETURNS CHAR(36)\n" | |||
| " LANGUAGE SQL\n" | |||
| " DETERMINISTIC\n" | |||
| " CONTAINS SQL\n" | |||
| " SQL SECURITY INVOKER\n" | |||
| "RETURN\n" | |||
| " IF(\n" | |||
| " _bin IS NULL,\n" | |||
| " NULL,\n" | |||
| " LCASE(CONCAT_WS('-',\n" | |||
| " HEX(SUBSTR(_bin, 13, 4)),\n" // time low | |||
| " HEX(SUBSTR(_bin, 11, 2)),\n" // time mid | |||
| " HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version | |||
| " HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence | |||
| " HEX(SUBSTR(_bin, 1, 6))\n" // node id | |||
| " )\n" | |||
| " )\n" | |||
| ")"; | |||
| exec_query(); | |||
| /* initialize tables */ | |||
| for (auto& table : tables) | |||
| { | |||
| assert(static_cast<bool>(table)); | |||
| table->init(context, init_stage::stage1); | |||
| } | |||
| for (auto& table : tables) | |||
| { | |||
| assert(static_cast<bool>(table)); | |||
| table->init(context, init_stage::stage2); | |||
| } | |||
| } | |||
| @@ -0,0 +1,413 @@ | |||
| #include <cppcore/misc/indent.h> | |||
| #include <cpphibernate/driver/mariadb/classes/tables/table.h> | |||
| #include <cpphibernate/driver/mariadb/classes/schema/schema.h> | |||
| using namespace ::cpphibernate; | |||
| using namespace ::cpphibernate::mariadb; | |||
| void table_t::init(const init_context& context, init_stage stage) const | |||
| { | |||
| switch (stage) | |||
| { | |||
| case init_stage::stage1: | |||
| { | |||
| auto& statement = get_statement_init_stage1(); | |||
| auto& connection = context.connection; | |||
| cpphibernate_log_debug("execute CREATE TABLE query: " << statement.query(connection)); | |||
| connection.execute(statement); | |||
| } | |||
| break; | |||
| case init_stage::stage2: | |||
| { | |||
| auto* statement = get_statement_init_stage2(); | |||
| auto& connection = context.connection; | |||
| if (!statement) | |||
| return; | |||
| cpphibernate_log_debug("execute ALTER TABLE query: " << statement->query(connection)); | |||
| connection.execute(*statement); | |||
| } | |||
| break; | |||
| case init_stage::unknown: | |||
| throw exception("Unkown or invalid init stage!"); | |||
| break; | |||
| } | |||
| } | |||
| ::cppmariadb::statement& table_t::get_statement_init_stage1() const | |||
| { | |||
| using namespace ::cppcore; | |||
| if (_statement_init_stage1) | |||
| return *_statement_init_stage1; | |||
| std::ostringstream os; | |||
| /* CREATE TABLE */ | |||
| os << "CREATE TABLE IF NOT EXISTS `" | |||
| << name | |||
| << "`" | |||
| << indent | |||
| << "(" | |||
| << incindent; | |||
| /* primary key */ | |||
| { | |||
| assert(primary_key_field); | |||
| auto& key_info = *primary_key_field; | |||
| auto args = key_info.create_arguments; | |||
| os << indent | |||
| << "`" | |||
| << key_info.name | |||
| << "` " | |||
| << key_info.type | |||
| << " NOT NULL" | |||
| << (args.empty() ? "" : " ") | |||
| << args | |||
| << ","; | |||
| } | |||
| /* base table key fields */ | |||
| if (static_cast<bool>(base_table)) | |||
| { | |||
| auto& base_table_info = *base_table; | |||
| assert(base_table_info.primary_key_field); | |||
| auto& key_info = *base_table_info.primary_key_field; | |||
| os << indent | |||
| << "`" | |||
| << key_info.name | |||
| << "` " | |||
| << key_info.type | |||
| << " NOT NULL,"; | |||
| } | |||
| /* foreign table one fields */ | |||
| for (auto& ptr : foreign_table_one_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| assert(field_info.referenced_table); | |||
| auto& referenced_table = *field_info.referenced_table; | |||
| if (referenced_table.is_used_in_container) | |||
| continue; | |||
| assert(referenced_table.primary_key_field); | |||
| auto& ref_key_info = *referenced_table.primary_key_field; | |||
| os << indent | |||
| << "`" | |||
| << ref_key_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` " | |||
| << ref_key_info.type | |||
| << (field_info.value_is_nullable | |||
| ? " NULL DEFAULT NULL," | |||
| : " NOT NULL,"); | |||
| } | |||
| /* foreign fields */ | |||
| for (auto& ptr : foreign_key_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| assert(field_info.table.primary_key_field); | |||
| auto& ref_key_info = *field_info.table.primary_key_field; | |||
| os << indent | |||
| << "`" | |||
| << field_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` " | |||
| << ref_key_info.type | |||
| << " NULL DEFAULT NULL,"; | |||
| if (field_info.value_is_ordered) | |||
| { | |||
| os << indent | |||
| << "`" | |||
| << field_info.table.name | |||
| << "_index_" | |||
| << field_info.name | |||
| << "` INT UNSIGNED NOT NULL,"; | |||
| } | |||
| } | |||
| /* data fields */ | |||
| for (auto& ptr : data_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| os << indent | |||
| << "`" | |||
| << field_info.name | |||
| << "` " | |||
| << field_info.type | |||
| << (field_info.value_is_nullable | |||
| ? " NULL DEFAULT NULL," | |||
| : " NOT NULL,"); | |||
| } | |||
| /* type field for derived tables */ | |||
| if (!derived_tables.empty() && | |||
| !base_table) | |||
| { | |||
| os << indent | |||
| << "`__type` INT UNSIGNED NOT NULL,"; | |||
| } | |||
| /* PRIMARY KEY */ | |||
| { | |||
| assert(primary_key_field); | |||
| auto& key_info = *primary_key_field; | |||
| os << indent | |||
| << "PRIMARY KEY ( `" | |||
| << key_info.name | |||
| << "` )"; | |||
| } | |||
| /* UNIQUE INDEX primary key */ | |||
| { | |||
| assert(primary_key_field); | |||
| auto& key_info = *primary_key_field; | |||
| os << ',' | |||
| << indent | |||
| << "UNIQUE INDEX `index_" | |||
| << key_info.name | |||
| << "` ( `" | |||
| << key_info.name | |||
| << "` ASC )"; | |||
| } | |||
| /* UNIQUE INDEX base table keys */ | |||
| if (base_table) | |||
| { | |||
| auto& table_info = *base_table; | |||
| assert(table_info.primary_key_field); | |||
| auto& key_info = *table_info.primary_key_field; | |||
| os << ',' | |||
| << indent | |||
| << "UNIQUE INDEX `index_" | |||
| << key_info.name | |||
| << "` ( `" | |||
| << key_info.name | |||
| << "` ASC )"; | |||
| } | |||
| /* INDEX foreign table one fields */ | |||
| for (auto& ptr : foreign_table_one_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| assert(field_info.referenced_table); | |||
| auto& referenced_table = *field_info.referenced_table; | |||
| if (referenced_table.is_used_in_container) | |||
| continue; | |||
| assert(referenced_table.primary_key_field); | |||
| auto& ref_key_info = *referenced_table.primary_key_field; | |||
| os << "," | |||
| << indent | |||
| << "INDEX `index_" | |||
| << ref_key_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` ( `" | |||
| << ref_key_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` ASC )"; | |||
| } | |||
| /* INDEX foreign fields */ | |||
| for (auto& ptr : foreign_key_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| os << "," | |||
| << indent | |||
| << "INDEX `index_" | |||
| << field_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` ( `" | |||
| << field_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "` ASC )"; | |||
| } | |||
| /* CREATE TABLE end */ | |||
| os << decindent | |||
| << indent | |||
| << ")" | |||
| << indent | |||
| << "ENGINE = InnoDB" | |||
| << indent | |||
| << "DEFAULT CHARACTER SET = utf8"; | |||
| _statement_init_stage1.reset(new ::cppmariadb::statement(os.str())); | |||
| return *_statement_init_stage1; | |||
| } | |||
| ::cppmariadb::statement* table_t::get_statement_init_stage2() const | |||
| { | |||
| using namespace ::cppcore; | |||
| if (_statement_init_stage2) | |||
| return _statement_init_stage2->empty() | |||
| ? nullptr | |||
| : _statement_init_stage2.get(); | |||
| std::ostringstream os; | |||
| /* ALTER TABLE */ | |||
| os << "ALTER TABLE `" | |||
| << name | |||
| << "`" | |||
| << incindent; | |||
| size_t index = 0; | |||
| /* CONSTRAINT base table */ | |||
| if (base_table) | |||
| { | |||
| assert(base_table->primary_key_field); | |||
| auto& ref_key_info = *base_table->primary_key_field; | |||
| if (index++) os << ","; | |||
| os << indent | |||
| << "ADD CONSTRAINT `fk_" | |||
| << name | |||
| << "_" | |||
| << ref_key_info.name | |||
| << "`" | |||
| << incindent | |||
| << indent | |||
| << "FOREIGN KEY IF NOT EXISTS (`" | |||
| << ref_key_info.name | |||
| << "`)" | |||
| << indent | |||
| << "REFERENCES `" | |||
| << ref_key_info.table.schema.name | |||
| << "`.`" | |||
| << ref_key_info.table.name | |||
| << "` (`" | |||
| << ref_key_info.name | |||
| << "`)" | |||
| << indent | |||
| << "ON DELETE CASCADE" | |||
| << indent | |||
| << "ON UPDATE CASCADE" | |||
| << decindent; | |||
| } | |||
| /* CONSTRAINT foreign table one fields */ | |||
| for (auto& ptr : foreign_table_one_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| assert(field_info.referenced_table); | |||
| auto& referenced_table = *field_info.referenced_table; | |||
| if (referenced_table.is_used_in_container) | |||
| continue; | |||
| assert(referenced_table.primary_key_field); | |||
| auto& ref_key_info = *referenced_table.primary_key_field; | |||
| if (index++) os << ","; | |||
| os << indent | |||
| << "ADD CONSTRAINT `fk_" | |||
| << name | |||
| << "_" | |||
| << ref_key_info.table.name | |||
| << "_" | |||
| << field_info.name | |||
| << "`" | |||
| << incindent | |||
| << indent | |||
| << "FOREIGN KEY IF NOT EXISTS (`" | |||
| << ref_key_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "`)" | |||
| << indent | |||
| << "REFERENCES `" | |||
| << ref_key_info.table.schema.name | |||
| << "`.`" | |||
| << ref_key_info.table.name | |||
| << "` (`" | |||
| << ref_key_info.name | |||
| << "`)" | |||
| << indent; | |||
| if (field_info.value_is_nullable) | |||
| os << "ON DELETE SET NULL"; | |||
| else | |||
| os << "ON DELETE CASCADE"; | |||
| os << indent | |||
| << "ON UPDATE NO ACTION" | |||
| << decindent; | |||
| } | |||
| /* CONSTRAINT foreign fields */ | |||
| for (auto& ptr : foreign_key_fields) | |||
| { | |||
| assert(static_cast<bool>(ptr)); | |||
| auto& field_info = *ptr; | |||
| assert(field_info.table.primary_key_field); | |||
| auto& ref_key_info = *field_info.table.primary_key_field; | |||
| if (index++) os << ","; | |||
| os << indent | |||
| << "ADD CONSTRAINT `fk_" | |||
| << name | |||
| << "_" | |||
| << field_info.table.name | |||
| << "_" | |||
| << field_info.name | |||
| << "`" | |||
| << incindent | |||
| << indent | |||
| << "FOREIGN KEY IF NOT EXISTS (`" | |||
| << field_info.table.name | |||
| << "_id_" | |||
| << field_info.name | |||
| << "`)" | |||
| << indent | |||
| << "REFERENCES `" | |||
| << ref_key_info.table.schema.name | |||
| << "`.`" | |||
| << ref_key_info.table.name | |||
| << "` (`" | |||
| << ref_key_info.name | |||
| << "`)" | |||
| << indent | |||
| << "ON DELETE SET NULL" | |||
| << indent | |||
| << "ON UPDATE NO ACTION" | |||
| << decindent; | |||
| } | |||
| _statement_init_stage2.reset(new ::cppmariadb::statement(index == 0 | |||
| ? std::string { } | |||
| : os.str())); | |||
| return _statement_init_stage2->empty() | |||
| ? nullptr | |||
| : _statement_init_stage2.get(); | |||
| } | |||
| @@ -11,7 +11,6 @@ namespace hana = ::boost::hana; | |||
| TEST(CppHibernateTests, init) | |||
| { | |||
| /* | |||
| StrictMock<mariadb_mock> mock; | |||
| expect_query(mock, "START TRANSACTION"); | |||
| @@ -178,7 +177,7 @@ TEST(CppHibernateTests, init) | |||
| expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage_item`\n" | |||
| "(\n" | |||
| " `tbl_double_usage_item_id` INT UNSIGNED NOT NULL,\n" | |||
| " `tbl_double_usage_item_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,\n" | |||
| " `tbl_double_usage_id_single_item` INT NULL DEFAULT NULL,\n" | |||
| " `tbl_double_usage_id_multiple_items` INT NULL DEFAULT NULL,\n" | |||
| " `tbl_double_usage_index_multiple_items` INT UNSIGNED NOT NULL,\n" | |||
| @@ -193,7 +192,7 @@ TEST(CppHibernateTests, init) | |||
| expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage`\n" | |||
| "(\n" | |||
| " `tbl_double_usage_id` INT NOT NULL,\n" | |||
| " `tbl_double_usage_id` INT NOT NULL AUTO_INCREMENT,\n" | |||
| " PRIMARY KEY ( `tbl_double_usage_id` ),\n" | |||
| " UNIQUE INDEX `index_tbl_double_usage_id` ( `tbl_double_usage_id` ASC )\n" | |||
| ")\n" | |||
| @@ -202,7 +201,7 @@ TEST(CppHibernateTests, init) | |||
| expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_double_usage_wrapper`\n" | |||
| "(\n" | |||
| " `tbl_double_usage_wrapper_id` INT NOT NULL,\n" | |||
| " `tbl_double_usage_wrapper_id` INT NOT NULL AUTO_INCREMENT,\n" | |||
| " `tbl_double_usage_id_double_usage` INT NULL DEFAULT NULL,\n" | |||
| " PRIMARY KEY ( `tbl_double_usage_wrapper_id` ),\n" | |||
| " UNIQUE INDEX `index_tbl_double_usage_wrapper_id` ( `tbl_double_usage_wrapper_id` ASC ),\n" | |||
| @@ -296,11 +295,9 @@ TEST(CppHibernateTests, init) | |||
| mock, | |||
| mysql_close( | |||
| reinterpret_cast<MYSQL*>(0x1111))); | |||
| */ | |||
| ::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
| auto context = make_context<mariadb_driver>(test_schema); | |||
| (void)context; | |||
| context.print(std::cout) << std::endl << std::endl; | |||
| // context.init(true); | |||
| context.connection(&connection); | |||
| context.init(true); | |||
| } | |||