@@ -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); | |||
} |