@@ -3,6 +3,14 @@ | |||
#include <boost/hana.hpp> | |||
#include <cpputils/mp/core.h> | |||
#define cpphibernate_debug | |||
#ifdef cpphibernate_debug | |||
# include <cpputils/logging/global.h> | |||
# define cpphibernate_debug_log(...) log_global_message(debug) << __VA_ARGS__ | |||
#else | |||
# define cpphibernate_debug_log(...) do { } while(0) | |||
#endif | |||
#define cpphibernate_equality_comparable() \ | |||
template<typename T_other> \ | |||
constexpr decltype(auto) operator==(T_other&&) const \ | |||
@@ -0,0 +1,6 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/helper/context.h> | |||
#include <cpphibernate/driver/mariadb/helper/key_properties.h> | |||
#include <cpphibernate/driver/mariadb/helper/transaction_lock.h> | |||
#include <cpphibernate/driver/mariadb/helper/type_properties.h> |
@@ -0,0 +1,20 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.fwd.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* init_context */ | |||
struct init_context | |||
{ | |||
const schema_t& schema; | |||
::cppmariadb::connection& connection; | |||
bool recreate; | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -0,0 +1,50 @@ | |||
#pragma once | |||
#include <cpphibernate/types.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpputils/container/nullable.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* key_properties */ | |||
template<typename T_key, typename = void> | |||
struct key_properties; | |||
template<> | |||
struct key_properties<uuid, void> | |||
{ | |||
using auto_generated = mp::c_false_t; | |||
using key_type = uuid; | |||
static constexpr decltype(auto) create_table_argument = ""; | |||
static constexpr decltype(auto) create_key_query = "SELECT Uuid()"; | |||
static constexpr decltype(auto) convert_to_open = "UuidToBin("; | |||
static constexpr decltype(auto) convert_to_close = ")"; | |||
static constexpr decltype(auto) convert_from_open = "BinToUuid("; | |||
static constexpr decltype(auto) convert_from_close = ")"; | |||
static bool is_default(const key_type& key) | |||
{ return key == key_type(); } | |||
}; | |||
template<typename T_key> | |||
struct key_properties<T_key, mp::enable_if<mp::is_integral<T_key>>> | |||
{ | |||
using auto_generated = mp::c_true_t; | |||
using key_type = T_key; | |||
static constexpr decltype(auto) create_table_argument = "AUTO_INCREMENT"; | |||
static constexpr decltype(auto) create_key_query = ""; | |||
static constexpr decltype(auto) convert_to_open = ""; | |||
static constexpr decltype(auto) convert_to_close = ""; | |||
static constexpr decltype(auto) convert_from_open = ""; | |||
static constexpr decltype(auto) convert_from_close = ""; | |||
static bool is_default(const key_type& key) | |||
{ return key == key_type(); } | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -0,0 +1,93 @@ | |||
#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 |
@@ -0,0 +1,346 @@ | |||
#pragma once | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/types.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpputils/container/nullable.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* value_t */ | |||
using value_t = utl::nullable<std::string>; | |||
/* type_properties */ | |||
template<typename T, typename = void> | |||
struct type_properties | |||
{ | |||
static constexpr void type() = delete; | |||
static T convert_to(const value_t&) = delete; | |||
static value_t convert_from(const T&) = delete; | |||
}; | |||
template<> | |||
struct type_properties<bool, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "BOOLEAN"; } | |||
static inline bool convert_to(const value_t& value) | |||
{ return utl::from_string<int>(*value); } | |||
static inline value_t convert_from(const bool& value) | |||
{ return utl::to_string(static_cast<int>(value)); } | |||
}; | |||
template<> | |||
struct type_properties<uint8_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "TINYINT UNSIGNED"; } | |||
static inline uint8_t convert_to(const value_t& value) | |||
{ return static_cast<uint8_t>(utl::from_string<int>(*value)); } | |||
static inline value_t convert_from(const uint8_t& value) | |||
{ return utl::to_string(static_cast<int>(value)); } | |||
}; | |||
template<> | |||
struct type_properties<int8_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "TINYINT"; } | |||
static inline int8_t convert_to(const value_t& value) | |||
{ return static_cast<int8_t>(utl::from_string<int>(*value)); } | |||
static inline value_t convert_from(const int8_t& value) | |||
{ return utl::to_string(static_cast<int>(value)); } | |||
}; | |||
template<> | |||
struct type_properties<uint16_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "SMALLINT UNSIGNED"; } | |||
static inline uint16_t convert_to(const value_t& value) | |||
{ return utl::from_string<uint16_t>(*value); } | |||
static inline value_t convert_from(const uint16_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<int16_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "SMALLINT"; } | |||
static inline int16_t convert_to(const value_t& value) | |||
{ return utl::from_string<int16_t>(*value); } | |||
static inline value_t convert_from(const int16_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<uint32_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "INT UNSIGNED"; } | |||
static inline uint32_t convert_to(const value_t& value) | |||
{ return utl::from_string<uint32_t>(*value); } | |||
static inline value_t convert_from(const uint32_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<int32_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "INT"; } | |||
static inline int32_t convert_to(const value_t& value) | |||
{ return utl::from_string<int32_t>(*value); } | |||
static inline value_t convert_from(const int32_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<uint64_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "BIGINT UNSIGNED"; } | |||
static inline uint64_t convert_to(const value_t& value) | |||
{ return utl::from_string<uint64_t>(*value); } | |||
static inline value_t convert_from(const uint64_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<int64_t, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "BIGINT"; } | |||
static inline int64_t convert_to(const value_t& value) | |||
{ return utl::from_string<int64_t>(*value); } | |||
static inline value_t convert_from(const int64_t& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<float, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "FLOAT"; } | |||
static inline float convert_to(const value_t& value) | |||
{ return utl::from_string<float>(*value); } | |||
static inline value_t convert_from(const float& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<double, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "DOUBLE"; } | |||
static inline double convert_to(const value_t& value) | |||
{ return utl::from_string<double>(*value); } | |||
static inline value_t convert_from(const double& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<uuid, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "BINARY(16)"; } | |||
static inline uuid convert_to(const value_t& value) | |||
{ return utl::from_string<uuid>(*value); } | |||
static inline value_t convert_from(const uuid& value) | |||
{ return utl::to_string(value); } | |||
}; | |||
template<> | |||
struct type_properties<std::string, void> | |||
{ | |||
static constexpr decltype(auto) type() | |||
{ return "VARCHAR(100)"; } | |||
static inline std::string convert_to(const value_t& value) | |||
{ return *value; } | |||
static inline value_t convert_from(const std::string& value) | |||
{ return value; } | |||
}; | |||
template<size_t N> | |||
struct type_properties<string<N>, void> | |||
{ | |||
static inline std::string make_type() | |||
{ return std::string("VARCHAR(") + utl::to_string(N) + ")"; } | |||
static inline decltype(auto) type() | |||
{ | |||
static const std::string v = make_type(); | |||
return v; | |||
} | |||
static inline std::string convert_to(const value_t& value) | |||
{ return *value; } | |||
static inline value_t convert_from(const std::string& value) | |||
{ return value; } | |||
}; | |||
template<typename T> | |||
struct type_properties<T, mp::enable_if<misc::is_nullable<mp::clean_type<T>>>> | |||
{ | |||
using nullable_type = T; | |||
using nullable_helper_type = misc::nullable_helper<nullable_type>; | |||
using value_type = typename nullable_helper_type::value_type; | |||
using value_type_props = type_properties<value_type>; | |||
static constexpr decltype(auto) type() | |||
{ return value_type_props::type(); } | |||
static inline nullable_type convert_to(const value_t& value) | |||
{ | |||
auto ret = nullable_helper_type::make(); | |||
if (value.has_value()) | |||
nullable_helper_type::fill(ret, value_type_props::convert_to(value)); | |||
return ret; | |||
} | |||
static inline value_t convert_from(const nullable_type& value) | |||
{ | |||
value_t ret; | |||
auto v = nullable_helper_type::get(value); | |||
if (v) | |||
ret = value_type_props::convert_from(*v); | |||
return ret; | |||
} | |||
}; | |||
template<typename T> | |||
struct type_properties<T, mp::enable_if<std::is_enum<mp::clean_type<T>>>> | |||
{ | |||
using enum_type = T; | |||
using base_type = typename std::underlying_type<enum_type>::type; | |||
static std::string make_type() | |||
{ | |||
std::ostringstream os; | |||
os << "ENUM ( "; | |||
auto e = enum_type::first; | |||
while (e <= enum_type::last) | |||
{ | |||
if (e != enum_type::first) | |||
os << ", "; | |||
os << "'" << utl::enum_conversion<enum_type>::to_string(e, false) << "'"; | |||
e = static_cast<enum_type>(static_cast<base_type>(e) + 1); | |||
} | |||
os << " )"; | |||
return os.str(); | |||
} | |||
static inline decltype(auto) type() | |||
{ | |||
static const std::string v = make_type(); | |||
return v; | |||
} | |||
static inline enum_type convert_to(const value_t& value) | |||
{ | |||
enum_type ret; | |||
if (!utl::enum_conversion<enum_type>::try_to_enum(*value, ret, false)) | |||
throw misc::hibernate_exception("unable to convert enum value!"); | |||
return ret; | |||
} | |||
static inline value_t convert_from(const enum_type& value) | |||
{ return utl::enum_conversion<enum_type>::to_string(value, false); } | |||
}; | |||
template<typename T> | |||
struct type_properties<T, mp::enable_if<mp::is_specialization_of<mp::clean_type<T>, utl::flags>>> | |||
{ | |||
using flags_type = T; | |||
using enum_type = typename flags_type::enum_type; | |||
using base_type = typename std::underlying_type<enum_type>::type; | |||
static inline std::string make_type() | |||
{ | |||
std::ostringstream os; | |||
os << "SET ( "; | |||
auto e = enum_type::first; | |||
while (e <= enum_type::last) | |||
{ | |||
if (e != enum_type::first) | |||
os << ", "; | |||
os << "'" << utl::to_string(e) << "'"; | |||
e = static_cast<enum_type>(static_cast<base_type>(e) + 1); | |||
} | |||
os << " )"; | |||
return os.str(); | |||
} | |||
static inline decltype(auto) type() | |||
{ | |||
static const std::string v = make_type(); | |||
return v; | |||
} | |||
static inline flags_type convert_to(const value_t& value) | |||
{ | |||
auto s = *value; | |||
auto c = s.c_str(); | |||
auto e = c + s.size(); | |||
auto p = c; | |||
flags_type ret; | |||
while (c <= e) | |||
{ | |||
if (c == e || *c == ',') | |||
{ | |||
if (c - p > 0) | |||
ret.set(utl::enum_conversion<enum_type>::to_enum(std::string(p, static_cast<size_t>(c - p)), true)); | |||
p = c + 1; | |||
} | |||
++c; | |||
} | |||
return ret; | |||
} | |||
static inline value_t convert_from(const flags_type& value) | |||
{ | |||
std::ostringstream os; | |||
bool first = true; | |||
for (auto e : value) | |||
{ | |||
if (first) first = false; | |||
else os << ","; | |||
utl::to_string(os, static_cast<int>(e)); | |||
} | |||
return os.str(); | |||
} | |||
}; | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -0,0 +1,3 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/impl/init.h> |
@@ -0,0 +1,44 @@ | |||
#pragma once | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* init_impl */ | |||
template<typename T_context, typename = void> | |||
struct init_impl | |||
{ | |||
using context_type = T_context; | |||
context_type context; | |||
constexpr init_impl(T_context&& p_context) | |||
: context(std::forward<T_context>(p_context)) | |||
{ } | |||
/* operator() */ | |||
inline void operator()() const | |||
{ | |||
auto& schema = context.schema; | |||
auto& connection = context.connection; | |||
transaction_lock trans(connection); | |||
schema.init(context); | |||
trans.commit(); | |||
} | |||
}; | |||
/* make_init_impl */ | |||
template<typename T_context> | |||
constexpr decltype(auto) make_init_impl(T_context&& context) | |||
{ return init_impl<T_context>(std::forward<T_context>(context)); } | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -2,11 +2,12 @@ | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/impl.h> | |||
#include <cpphibernate/driver/mariadb/schema.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
struct mariadb_driver_t | |||
{ | |||
private: | |||
@@ -23,11 +24,16 @@ beg_namespace_cpphibernate_driver_mariadb | |||
cpphibernate_copyable(mariadb_driver_t, delete); | |||
cpphibernate_moveable(mariadb_driver_t, default); | |||
inline decltype(auto) connection() | |||
{ return _connection; } | |||
inline auto& schema() | |||
{ return _schema; } | |||
protected: | |||
inline void init_impl(bool recreate) const | |||
{ | |||
make_init_impl(init_context | |||
{ | |||
_schema, | |||
_connection, | |||
recreate | |||
})(); | |||
} | |||
}; | |||
} |
@@ -12,4 +12,6 @@ | |||
#include <cpphibernate/driver/mariadb/schema/fields.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.h> | |||
#include <cpphibernate/driver/mariadb/schema/table.h> | |||
#include <cpphibernate/driver/mariadb/schema/tables.h> | |||
#include <cpphibernate/driver/mariadb/schema/tables.h> | |||
#include <cpphibernate/driver/mariadb/schema/field.inl> |
@@ -5,13 +5,14 @@ | |||
#include <cpphibernate/schema/field.h> | |||
#include <cpphibernate/schema/table.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include <cpphibernate/driver/mariadb/helper.h> | |||
#include <cpphibernate/driver/mariadb/schema/field.fwd.h> | |||
#include <cpphibernate/driver/mariadb/schema/table.fwd.h> | |||
#include <cpphibernate/driver/mariadb/schema/attributes.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* field_t */ | |||
struct field_t | |||
@@ -42,9 +43,13 @@ beg_namespace_cpphibernate_driver_mariadb | |||
, table (nullptr) | |||
, referenced_table (nullptr) | |||
{ } | |||
virtual ~field_t() = default; | |||
virtual ~field_t() { }; | |||
void print(std::ostream& os) const; | |||
/* properties */ | |||
virtual std::string type () const; | |||
virtual std::string create_table_arguments () const; | |||
}; | |||
/* simple_field_t */ | |||
@@ -72,9 +77,17 @@ beg_namespace_cpphibernate_driver_mariadb | |||
struct value_field_t | |||
: public simple_field_t<T_schema, T_field> | |||
{ | |||
using base_type = simple_field_t<T_schema, 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 mp::decay_t<field_type>::getter_type; | |||
using dataset_type = typename getter_type::dataset_type; | |||
using value_type = typename getter_type::value_type; | |||
using type_props = type_properties<value_type>; | |||
using base_type::base_type; | |||
virtual std::string type() const override; | |||
}; | |||
/* primary_key_field_t */ | |||
@@ -83,9 +96,15 @@ beg_namespace_cpphibernate_driver_mariadb | |||
struct primary_key_field_t | |||
: public value_field_t<T_schema, T_field> | |||
{ | |||
using base_type = value_field_t<T_schema, T_field>; | |||
using base_type = value_field_t<T_schema, T_field>; | |||
using schema_type = typename base_type::schema_type; | |||
using field_type = typename base_type::field_type; | |||
using value_type = typename base_type::value_type; | |||
using key_props = key_properties<value_type>; | |||
using base_type::base_type; | |||
virtual std::string create_table_arguments() const override; | |||
}; | |||
/* data_field_t */ | |||
@@ -179,15 +198,21 @@ beg_namespace_cpphibernate_driver_mariadb | |||
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, | |||
ret.schema_name = schema.name, | |||
ret.table_name = table.name, | |||
ret.field_name = field.name, | |||
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; | |||
ret.schema_name = schema.name; | |||
ret.table_name = table.name; | |||
ret.field_name = field.name; | |||
ret.attributes = make_attributes(field.attributes); | |||
hana::eval_if( | |||
hana::equal(hana::type_c<return_type>, hana::type_c<primary_key_type>), | |||
[&ret](){ | |||
ret.field_name = ret.table_name + "_" + ret.field_name; | |||
}, [](){ }); | |||
return ret; | |||
} | |||
}; | |||
@@ -0,0 +1,21 @@ | |||
#pragma once | |||
#include <cpphibernate/driver/mariadb/schema/field.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* 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(); } | |||
/* 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; } | |||
} | |||
end_namespace_cpphibernate_driver_mariadb |
@@ -2,12 +2,13 @@ | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/driver/mariadb/helper/context.h> | |||
#include <cpphibernate/driver/mariadb/schema/tables.h> | |||
#include <cpphibernate/driver/mariadb/schema/schema.fwd.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* schema_t */ | |||
struct schema_t | |||
@@ -24,6 +25,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
void update (); | |||
void print (std::ostream& os) const; | |||
/* CRUD */ | |||
void init(const init_context& context) const; | |||
}; | |||
namespace __impl | |||
@@ -3,16 +3,18 @@ | |||
#include <memory> | |||
#include <vector> | |||
#include <cppmariadb.h> | |||
#include <cpphibernate/misc.h> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/schema/table.h> | |||
#include <cpphibernate/schema/schema.h> | |||
#include <cpphibernate/driver/mariadb/schema/fields.h> | |||
#include <cpphibernate/driver/mariadb/schema/table.fwd.h> | |||
#include <cpphibernate/driver/mariadb/helper/context.h> | |||
beg_namespace_cpphibernate_driver_mariadb | |||
{ | |||
/* table_t */ | |||
struct table_t | |||
@@ -56,9 +58,23 @@ beg_namespace_cpphibernate_driver_mariadb | |||
, foreign_table_many_fields () | |||
, data_fields () | |||
{ } | |||
virtual ~table_t() = default; | |||
virtual ~table_t() { }; | |||
void print(std::ostream& os) const; | |||
/* CRUD */ | |||
inline void init(const init_context& context) const | |||
{ return init_intern(context); } | |||
private: | |||
using statement_ptr = std::unique_ptr<::cppmariadb::statement>; | |||
mutable statement_ptr _statement_create_table; | |||
::cppmariadb::statement& get_statement_create_table() const; | |||
protected: | |||
void init_intern(const init_context& context) const; | |||
}; | |||
/* table_simple_t */ | |||
@@ -70,7 +86,7 @@ beg_namespace_cpphibernate_driver_mariadb | |||
using schema_type = T_schema; | |||
using table_type = T_table; | |||
using base_dataset_type = T_base_dataset; | |||
const schema_type& schema; | |||
const table_type& table; | |||
@@ -98,9 +114,9 @@ beg_namespace_cpphibernate_driver_mariadb | |||
using schema_type = T_schema; | |||
using table_type = T_table; | |||
using base_dataset_type = T_base_dataset; | |||
using base_type::base_type; | |||
const schema_type& schema; | |||
const table_type& table; | |||
}; | |||
@@ -2,5 +2,6 @@ | |||
#include <cpphibernate/misc/general.h> | |||
#include <cpphibernate/misc/meta.h> | |||
#include <cpphibernate/misc/nullable_helper.h> | |||
#include <cpphibernate/misc/print.h> | |||
#include <cpphibernate/misc/wrap.h> |
@@ -0,0 +1,76 @@ | |||
#pragma once | |||
#include <memory> | |||
#include <cpphibernate/config.h> | |||
#include <cpphibernate/misc/meta.h> | |||
#include <cpputils/container/nullable.h> | |||
beg_namespace_cpphibernate_misc | |||
{ | |||
/* nullable_helper */ | |||
template<typename T_nullable, typename = void> | |||
struct nullable_helper | |||
{ | |||
using nullable_type = T_nullable; | |||
using value_type = real_dataset_t<nullable_type>; | |||
static value_type* get (nullable_type&) = delete; | |||
static void reset (nullable_type&, value_type* value = nullptr) = delete; | |||
}; | |||
/* nullable_helper - utl::nullable */ | |||
template<typename T_value> | |||
struct nullable_helper<utl::nullable<T_value>, void> | |||
{ | |||
using nullable_type = utl::nullable<T_value>; | |||
using value_type = T_value; | |||
static inline value_type* get(nullable_type& x) | |||
{ | |||
return x.has_value() ? &x.value() : nullptr; | |||
} | |||
static void reset(nullable_type& x, value_type* value = nullptr) | |||
{ | |||
if (value) x.reset(); | |||
else x = *value; | |||
} | |||
}; | |||
/* nullable_helper - std::unique_ptr */ | |||
template<typename T_value> | |||
struct nullable_helper<std::unique_ptr<T_value>, void> | |||
{ | |||
using nullable_type = std::unique_ptr<T_value>; | |||
using value_type = T_value; | |||
static inline value_type* get(nullable_type& x) | |||
{ return x.get(); } | |||
static void reset(nullable_type& x, value_type* value = nullptr) | |||
{ return x.reset(value); } | |||
}; | |||
/* nullable_helper - std::shared_ptr */ | |||
template<typename T_value> | |||
struct nullable_helper<std::shared_ptr<T_value>, void> | |||
{ | |||
using nullable_type = std::shared_ptr<T_value>; | |||
using value_type = T_value; | |||
static inline value_type* get(nullable_type& x) | |||
{ return x.get(); } | |||
static void reset(nullable_type& x, value_type* value = nullptr) | |||
{ return x.reset(value); } | |||
}; | |||
} | |||
end_namespace_cpphibernate_misc |
@@ -9,6 +9,7 @@ | |||
#include <cpphibernate/driver/mariadb/schema/field.h> | |||
#include <cpphibernate/driver/mariadb/schema/table.h> | |||
using namespace ::std; | |||
using namespace ::utl; | |||
using namespace ::cpphibernate::driver::mariadb_impl; | |||
@@ -28,4 +29,15 @@ void field_t::print(std::ostream& os) const | |||
<< 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() const \ | |||
{ \ | |||
throw misc::hibernate_exception( \ | |||
std::string("'") + table_name + "." + field_name + \ | |||
"' does not implement the " #p_name "() method!"); \ | |||
} | |||
throw_not_implemented(string, type) | |||
throw_not_implemented(string, create_table_arguments) |
@@ -1,5 +1,5 @@ | |||
#include <string> | |||
#include <iostream> | |||
#include <sstream> | |||
#include <cpputils/misc/enum.h> | |||
#include <cpputils/misc/string.h> | |||
@@ -109,4 +109,83 @@ void schema_t::print(std::ostream& os) const | |||
}) | |||
<< decindent | |||
<< indent << '}'; | |||
} | |||
} | |||
#define exec_query() \ | |||
do { \ | |||
cpphibernate_debug_log("execute init query: " << ss.str()); \ | |||
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 `" | |||
<< schema_name | |||
<< "`"; | |||
exec_query(); | |||
} | |||
/* create schema */ | |||
ss << "CREATE SCHEMA IF NOT EXISTS `" | |||
<< schema_name | |||
<< "` DEFAULT CHARACTER SET utf8"; | |||
exec_query(); | |||
/* use schema */ | |||
ss << "USE `" | |||
<< schema_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" | |||
" 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" | |||
")"; | |||
exec_query(); | |||
/* initialize tables */ | |||
for (auto& kvp : tables) | |||
{ | |||
assert(kvp.second); | |||
kvp.second->init(context); | |||
} | |||
} | |||
#undef exec_query |
@@ -46,4 +46,311 @@ void table_t::print(std::ostream& os) const | |||
}) | |||
<< decindent | |||
<< indent << '}'; | |||
} | |||
::cppmariadb::statement& table_t::get_statement_create_table() const | |||
{ | |||
if (_statement_create_table) | |||
return *_statement_create_table; | |||
std::ostringstream os; | |||
/* CREATE TABLE */ | |||
os << "CREATE TABLE IF NOT EXISTS `" | |||
<< table_name | |||
<< "`" | |||
<< indent | |||
<< "(" | |||
<< incindent; | |||
/* primary key */ | |||
{ | |||
assert(primary_key_field); | |||
auto& key_info = *primary_key_field; | |||
auto args = key_info.create_table_arguments(); | |||
os << indent | |||
<< "`" | |||
<< key_info.field_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.field_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); | |||
assert(field_info.referenced_table->primary_key_field); | |||
auto& ref_key_info = *field_info.referenced_table->primary_key_field; | |||
os << indent | |||
<< "`" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_info.field_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); | |||
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.field_name | |||
<< "` " | |||
<< ref_key_info.type() | |||
<< " NULL DEFAULT NULL,"; | |||
} | |||
/* data fields */ | |||
for (auto& ptr : data_fields) | |||
{ | |||
assert(static_cast<bool>(ptr)); | |||
auto& field_info = *ptr; | |||
os << indent | |||
<< "`" | |||
<< field_info.field_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 */ | |||
{ | |||
os << indent | |||
<< "PRIMARY KEY ( `" | |||
<< primary_key_field->field_name | |||
<< "` )"; | |||
} | |||
/* UNIQUE INDEX primary key */ | |||
os << ',' | |||
<< indent | |||
<< "UNIQUE INDEX `index_" | |||
<< primary_key_field->field_name | |||
<< "` ( `" | |||
<< primary_key_field->field_name | |||
<< "` ASC )"; | |||
/* UNIQUE INDEX base table keys */ | |||
if (base_table) | |||
{ | |||
auto& table_info = *base_table; | |||
auto& key_info = *table_info.primary_key_field; | |||
os << ',' | |||
<< indent | |||
<< "UNIQUE INDEX `index_" | |||
<< key_info.field_name | |||
<< "` ( `" | |||
<< key_info.field_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); | |||
assert(field_info.referenced_table->primary_key_field); | |||
auto& ref_key_info = *field_info.referenced_table->primary_key_field; | |||
os << "," | |||
<< indent | |||
<< "INDEX `index_" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "` ( `" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_info.field_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.field_name | |||
<< "` ( `" | |||
<< field_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "` ASC )"; | |||
} | |||
/* CONSTRAINT base table */ | |||
if (base_table) | |||
{ | |||
assert(base_table->primary_key_field); | |||
auto& ref_key_info = *base_table->primary_key_field; | |||
os << "," | |||
<< indent | |||
<< "CONSTRAINT `fk_" | |||
<< table_name | |||
<< "_to_" | |||
<< ref_key_info.field_name | |||
<< "`" | |||
<< incindent | |||
<< indent | |||
<< "FOREIGN KEY (`" | |||
<< ref_key_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "REFERENCES `" | |||
<< ref_key_info.schema_name | |||
<< "`.`" | |||
<< ref_key_info.table_name | |||
<< "` (`" | |||
<< ref_key_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "ON DELETE CASCADE" | |||
<< indent | |||
<< "ON UPDATE NO ACTION" | |||
<< 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); | |||
assert(field_info.referenced_table->primary_key_field); | |||
auto& ref_key_info = *field_info.referenced_table->primary_key_field; | |||
os << "," | |||
<< indent | |||
<< "CONSTRAINT `fk_" | |||
<< table_name | |||
<< "_to_" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "`" | |||
<< incindent | |||
<< indent | |||
<< "FOREIGN KEY (`" | |||
<< ref_key_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "REFERENCES `" | |||
<< ref_key_info.schema_name | |||
<< "`.`" | |||
<< ref_key_info.table_name | |||
<< "` (`" | |||
<< ref_key_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "ON DELETE CASCADE" | |||
<< 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); | |||
assert(field_info.table->primary_key_field); | |||
auto& ref_key_info = *field_info.table->primary_key_field; | |||
os << "," | |||
<< indent | |||
<< "CONSTRAINT `fk_" | |||
<< table_name | |||
<< "_" | |||
<< field_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "`" | |||
<< incindent | |||
<< indent | |||
<< "FOREIGN KEY (`" | |||
<< field_info.table_name | |||
<< "_id_" | |||
<< field_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "REFERENCES `" | |||
<< ref_key_info.schema_name | |||
<< "`.`" | |||
<< ref_key_info.table_name | |||
<< "` (`" | |||
<< ref_key_info.field_name | |||
<< "`)" | |||
<< indent | |||
<< "ON DELETE SET NULL" | |||
<< indent | |||
<< "ON UPDATE NO ACTION" | |||
<< decindent; | |||
} | |||
/* CREATE TABLE end */ | |||
os << decindent | |||
<< indent | |||
<< ")" | |||
<< indent | |||
<< "ENGINE = InnoDB" | |||
<< indent | |||
<< "DEFAULT CHARACTER SET = utf8"; | |||
_statement_create_table.reset(new ::cppmariadb::statement(os.str())); | |||
return *_statement_create_table; | |||
} | |||
void table_t::init_intern(const init_context& context) const | |||
{ | |||
auto& statement = get_statement_create_table(); | |||
auto& connection = context.connection; | |||
cpphibernate_debug_log("execute init query: " << statement.query(connection)); | |||
connection.execute(statement); | |||
} |
@@ -0,0 +1,226 @@ | |||
#include <gtest/gtest.h> | |||
#include <cpphibernate/driver/mariadb.h> | |||
#include "test_schema.h" | |||
#include "mariadb_mock.h" | |||
using namespace ::testing; | |||
using namespace ::cpphibernate; | |||
template<typename T_mock> | |||
inline void expect_query(T_mock& mock, const std::string& query) | |||
{ | |||
EXPECT_CALL( | |||
mock, | |||
mysql_real_query( | |||
reinterpret_cast<MYSQL*>(0x1111), | |||
StrEq(query), | |||
query.size())); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_store_result( | |||
reinterpret_cast<MYSQL*>(0x1111))) | |||
.WillOnce(Return(reinterpret_cast<MYSQL_RES*>(0x2222))); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_free_result( | |||
reinterpret_cast<MYSQL_RES*>(0x2222))); | |||
} | |||
TEST(CppHibernateTests, init) | |||
{ | |||
StrictMock<MariaDbMock> mock; | |||
InSequence seq; | |||
expect_query(mock, "START TRANSACTION"); | |||
expect_query(mock, "DROP DATABASE IF EXISTS `test`"); | |||
expect_query(mock, "CREATE SCHEMA IF NOT EXISTS `test` DEFAULT CHARACTER SET utf8"); | |||
expect_query(mock, "USE `test`"); | |||
expect_query(mock, "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" | |||
" SUBSTR(_uuid, 20, 4),\n" | |||
" SUBSTR(_uuid, 15, 4),\n" | |||
" SUBSTR(_uuid, 10, 4),\n" | |||
" SUBSTR(_uuid, 1, 8)\n" | |||
" )\n" | |||
")"); | |||
expect_query(mock, "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" | |||
" LCASE(CONCAT_WS('-',\n" | |||
" HEX(SUBSTR(_bin, 13, 4)),\n" | |||
" HEX(SUBSTR(_bin, 11, 2)),\n" | |||
" HEX(SUBSTR(_bin, 9, 2)),\n" | |||
" HEX(SUBSTR(_bin, 7, 2)),\n" | |||
" HEX(SUBSTR(_bin, 1, 6))\n" | |||
" )\n" | |||
")"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_test1`\n" | |||
"(\n" | |||
" `tbl_test1_id` BINARY(16) NOT NULL,\n" | |||
" `str_data` VARCHAR(100) NOT NULL,\n" | |||
" `str64_data` VARCHAR(64) NOT NULL,\n" | |||
" `u32_nullable` INT UNSIGNED NULL DEFAULT NULL,\n" | |||
" `u32_ptr_u` INT UNSIGNED NULL DEFAULT NULL,\n" | |||
" `u32_ptr_s` INT UNSIGNED NULL DEFAULT NULL,\n" | |||
" PRIMARY KEY ( `tbl_test1_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_test1_id` ( `tbl_test1_id` ASC )\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_test2`\n" | |||
"(\n" | |||
" `tbl_test2_id` BINARY(16) NOT NULL,\n" | |||
" `u8_data` TINYINT UNSIGNED NOT NULL,\n" | |||
" `i8_data` TINYINT NOT NULL,\n" | |||
" `u16_data` SMALLINT UNSIGNED NOT NULL,\n" | |||
" `i16_data` SMALLINT NOT NULL,\n" | |||
" PRIMARY KEY ( `tbl_test2_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_test2_id` ( `tbl_test2_id` ASC )\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_test3`\n" | |||
"(\n" | |||
" `tbl_test3_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_derived3_id_test3_list` BINARY(16) NULL DEFAULT NULL,\n" | |||
" `tbl_derived3_id_test3_vector` BINARY(16) NULL DEFAULT NULL,\n" | |||
" `u32_data` INT UNSIGNED NOT NULL,\n" | |||
" `i32_data` INT NOT NULL,\n" | |||
" `u64_data` BIGINT UNSIGNED NOT NULL,\n" | |||
" `i64_data` BIGINT NOT NULL,\n" | |||
" PRIMARY KEY ( `tbl_test3_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_test3_id` ( `tbl_test3_id` ASC ),\n" | |||
" INDEX `index_tbl_derived3_id_test3_list` ( `tbl_derived3_id_test3_list` ASC ),\n" | |||
" INDEX `index_tbl_derived3_id_test3_vector` ( `tbl_derived3_id_test3_vector` ASC ),\n" | |||
" CONSTRAINT `fk_tbl_test3_tbl_derived3_id_test3_list`\n" | |||
" FOREIGN KEY (`tbl_derived3_id_test3_list`)\n" | |||
" REFERENCES `test`.`tbl_derived3` (`tbl_derived3_id`)\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" CONSTRAINT `fk_tbl_test3_tbl_derived3_id_test3_vector`\n" | |||
" FOREIGN KEY (`tbl_derived3_id_test3_vector`)\n" | |||
" REFERENCES `test`.`tbl_derived3` (`tbl_derived3_id`)\n" | |||
" ON DELETE SET NULL\n" | |||
" ON UPDATE NO ACTION\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_base`\n" | |||
"(\n" | |||
" `tbl_base_id` BINARY(16) NOT NULL,\n" | |||
" `name` VARCHAR(100) NOT NULL,\n" | |||
" `__type` INT UNSIGNED NOT NULL,\n" | |||
" PRIMARY KEY ( `tbl_base_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_base_id` ( `tbl_base_id` ASC )\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_derived1`\n" | |||
"(\n" | |||
" `tbl_derived1_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_base_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_test1_id_test1_data` BINARY(16) NOT NULL,\n" | |||
" `enum_data` ENUM ( 'test0', 'test1', 'test2', 'test3' ) NOT NULL,\n" | |||
" PRIMARY KEY ( `tbl_derived1_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_derived1_id` ( `tbl_derived1_id` ASC ),\n" | |||
" UNIQUE INDEX `index_tbl_base_id` ( `tbl_base_id` ASC ),\n" | |||
" INDEX `index_tbl_test1_id_test1_data` ( `tbl_test1_id_test1_data` ASC ),\n" | |||
" CONSTRAINT `fk_tbl_derived1_to_tbl_base_id`\n" | |||
" FOREIGN KEY (`tbl_base_id`)\n" | |||
" REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" CONSTRAINT `fk_tbl_derived1_to_tbl_test1_id_test1_data`\n" | |||
" FOREIGN KEY (`tbl_test1_id_test1_data`)\n" | |||
" REFERENCES `test`.`tbl_test1` (`tbl_test1_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_derived2`\n" | |||
"(\n" | |||
" `tbl_derived2_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_base_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_test2_id_test2_nullable` BINARY(16) NULL DEFAULT NULL,\n" | |||
" `tbl_test2_id_test2_ptr_u` BINARY(16) NULL DEFAULT NULL,\n" | |||
" `tbl_test2_id_test2_ptr_s` BINARY(16) NULL DEFAULT NULL,\n" | |||
" PRIMARY KEY ( `tbl_derived2_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_derived2_id` ( `tbl_derived2_id` ASC ),\n" | |||
" UNIQUE INDEX `index_tbl_base_id` ( `tbl_base_id` ASC ),\n" | |||
" INDEX `index_tbl_test2_id_test2_nullable` ( `tbl_test2_id_test2_nullable` ASC ),\n" | |||
" INDEX `index_tbl_test2_id_test2_ptr_u` ( `tbl_test2_id_test2_ptr_u` ASC ),\n" | |||
" INDEX `index_tbl_test2_id_test2_ptr_s` ( `tbl_test2_id_test2_ptr_s` ASC ),\n" | |||
" CONSTRAINT `fk_tbl_derived2_to_tbl_base_id`\n" | |||
" FOREIGN KEY (`tbl_base_id`)\n" | |||
" REFERENCES `test`.`tbl_base` (`tbl_base_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_nullable`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_nullable`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_u`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_ptr_u`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION,\n" | |||
" CONSTRAINT `fk_tbl_derived2_to_tbl_test2_id_test2_ptr_s`\n" | |||
" FOREIGN KEY (`tbl_test2_id_test2_ptr_s`)\n" | |||
" REFERENCES `test`.`tbl_test2` (`tbl_test2_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_derived3`\n" | |||
"(\n" | |||
" `tbl_derived3_id` BINARY(16) NOT NULL,\n" | |||
" `tbl_derived1_id` BINARY(16) NOT NULL,\n" | |||
" PRIMARY KEY ( `tbl_derived3_id` ),\n" | |||
" UNIQUE INDEX `index_tbl_derived3_id` ( `tbl_derived3_id` ASC ),\n" | |||
" UNIQUE INDEX `index_tbl_derived1_id` ( `tbl_derived1_id` ASC ),\n" | |||
" CONSTRAINT `fk_tbl_derived3_to_tbl_derived1_id`\n" | |||
" FOREIGN KEY (`tbl_derived1_id`)\n" | |||
" REFERENCES `test`.`tbl_derived1` (`tbl_derived1_id`)\n" | |||
" ON DELETE CASCADE\n" | |||
" ON UPDATE NO ACTION\n" | |||
")\n" | |||
"ENGINE = InnoDB\n" | |||
"DEFAULT CHARACTER SET = utf8"); | |||
expect_query(mock, "COMMIT"); | |||
EXPECT_CALL( | |||
mock, | |||
mysql_close( | |||
reinterpret_cast<MYSQL*>(0x1111))); | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
context.init(true); | |||
} |
@@ -1,62 +1,91 @@ | |||
#include <gtest/gtest.h> | |||
#include <cpphibernate.h> | |||
#include <cpphibernate/driver/mariadb.h> | |||
using namespace ::cpphibernate; | |||
enum class test_enum | |||
{ | |||
test0, | |||
test1, | |||
test2, | |||
test3, | |||
first = test0, | |||
last = test3, | |||
}; | |||
DEFINE_ENUM_TO_STRING_MAP( | |||
test_enum, | |||
{ test_enum::test0, "test0" }, | |||
{ test_enum::test1, "test1" }, | |||
{ test_enum::test2, "test2" }, | |||
{ test_enum::test3, "test3" } | |||
); | |||
DEFINE_STRING_TO_ENUM_MAP( | |||
test_enum, | |||
invariant_string_less, | |||
{ "test0", test_enum::test0 }, | |||
{ "test1", test_enum::test1 }, | |||
{ "test2", test_enum::test2 }, | |||
{ "test3", test_enum::test3 } | |||
); | |||
struct test1 | |||
{ | |||
uuid id; | |||
std::string str_data; | |||
string<64> str64_data; | |||
::cpphibernate::uuid id; | |||
std::string str_data; | |||
::cpphibernate::string<64> str64_data; | |||
utl::nullable<uint32_t> u32_nullable; | |||
std::unique_ptr<uint32_t> u32_ptr_u; | |||
std::shared_ptr<uint32_t> u32_ptr_s; | |||
}; | |||
struct test2 | |||
{ | |||
uuid id; | |||
uint8_t u8_data; | |||
int8_t i8_data; | |||
uint16_t u16_data; | |||
int16_t i16_data; | |||
::cpphibernate::uuid id; | |||
uint8_t u8_data; | |||
int8_t i8_data; | |||
uint16_t u16_data; | |||
int16_t i16_data; | |||
}; | |||
struct test3 | |||
{ | |||
uuid id; | |||
uint32_t u32_data; | |||
int32_t i32_data; | |||
uint64_t u64_data; | |||
int64_t i64_data; | |||
::cpphibernate::uuid id; | |||
uint32_t u32_data; | |||
int32_t i32_data; | |||
uint64_t u64_data; | |||
int64_t i64_data; | |||
}; | |||
struct base | |||
{ | |||
uuid id; | |||
std::string name; | |||
::cpphibernate::uuid id; | |||
std::string name; | |||
}; | |||
struct derived1 | |||
: public base | |||
{ | |||
uuid derived1_id; | |||
test1 test1_data; | |||
::cpphibernate::uuid derived1_id; | |||
test1 test1_data; | |||
test_enum enum_data; | |||
}; | |||
struct derived2 | |||
: public base | |||
{ | |||
uuid derived2_id; | |||
utl::nullable<test2> test2_nullable; | |||
std::unique_ptr<test2> test2_ptr_u; | |||
std::shared_ptr<test2> test2_ptr_s; | |||
::cpphibernate::uuid derived2_id; | |||
utl::nullable<test2> test2_nullable; | |||
std::unique_ptr<test2> test2_ptr_u; | |||
std::shared_ptr<test2> test2_ptr_s; | |||
}; | |||
struct derived3 | |||
: public derived1 | |||
{ | |||
uuid derived3_id; | |||
std::list<test3> test3_list; | |||
std::vector<test3> test3_vector; | |||
::cpphibernate::uuid derived3_id; | |||
std::list<test3> test3_list; | |||
std::vector<test3> test3_vector; | |||
}; | |||
constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||
@@ -67,7 +96,10 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||
1, | |||
cpphibernate_make_id (&test1::id), | |||
cpphibernate_make_field (test1, str_data), | |||
cpphibernate_make_field (test1, str64_data) | |||
cpphibernate_make_field (test1, str64_data), | |||
cpphibernate_make_field (test1, u32_nullable), | |||
cpphibernate_make_field (test1, u32_ptr_u), | |||
cpphibernate_make_field (test1, u32_ptr_s) | |||
), | |||
cpphibernate_make_table_name( | |||
tbl_test2, | |||
@@ -102,7 +134,8 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||
derived1, | |||
11, | |||
cpphibernate_make_id (&derived1::derived1_id), | |||
cpphibernate_make_field (derived1, test1_data) | |||
cpphibernate_make_field (derived1, test1_data), | |||
cpphibernate_make_field (derived1, enum_data) | |||
), | |||
cpphibernate_make_table_name( | |||
tbl_derived2, | |||
@@ -121,11 +154,4 @@ constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||
cpphibernate_make_field (derived3, test3_list), | |||
cpphibernate_make_field (derived3, test3_vector) | |||
) | |||
); | |||
TEST(CppHibernateTests, fuuu) | |||
{ | |||
::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1234)); | |||
auto context = make_context<driver::mariadb>(test_schema, connection); | |||
std::cout << context.schema() << std::endl; | |||
} | |||
); |