Browse Source

* refactored read methods for mariadb driver

* implemented read methods of foreign many tables for mariadb driver
master
bergmann 5 years ago
parent
commit
f556a98db3
17 changed files with 1062 additions and 289 deletions
  1. +41
    -19
      include/cpphibernate/driver/mariadb/helper/context.h
  2. +45
    -21
      include/cpphibernate/driver/mariadb/helper/context.inl
  3. +204
    -61
      include/cpphibernate/driver/mariadb/impl/read.h
  4. +0
    -1
      include/cpphibernate/driver/mariadb/impl/where.h
  5. +10
    -2
      include/cpphibernate/driver/mariadb/mariadb.h
  6. +1
    -1
      include/cpphibernate/driver/mariadb/schema/field.fwd.h
  7. +4
    -2
      include/cpphibernate/driver/mariadb/schema/field.h
  8. +18
    -33
      include/cpphibernate/driver/mariadb/schema/field.inl
  9. +1
    -1
      include/cpphibernate/driver/mariadb/schema/table.fwd.h
  10. +7
    -1
      include/cpphibernate/driver/mariadb/schema/table.h
  11. +7
    -2
      include/cpphibernate/driver/mariadb/schema/table.inl
  12. +1
    -0
      include/cpphibernate/misc.h
  13. +2
    -0
      include/cpphibernate/misc/general.h
  14. +19
    -4
      include/cpphibernate/misc/nullable_helper.h
  15. +2
    -1
      src/driver/mariadb/schema/field.cpp
  16. +325
    -87
      src/driver/mariadb/schema/table.cpp
  17. +375
    -53
      test/cpphibernate_read.cpp

+ 41
- 19
include/cpphibernate/driver/mariadb/helper/context.h View File

@@ -50,9 +50,8 @@ beg_namespace_cpphibernate_driver_mariadb
template<typename T_context, typename T_data>
constexpr decltype(auto) operator()(const T_context& context, T_data& data) const
{
auto new_context = context;
new_context.data_id = misc::get_type_id(hana::type_c<mp::decay_t<T_data>>);
new_context.data = &data;
auto new_context = context;
new_context.set(data);
return new_context;
}
};
@@ -68,12 +67,26 @@ beg_namespace_cpphibernate_driver_mariadb
private:
friend __impl::change_context_impl;

size_t data_id;
void* data;
mutable size_t _dataset_id;
mutable void* _dataset;
mutable const table_t* _table;

protected:
template<typename T_dataset>
inline void* set(T_dataset& dataset, size_t dataset_id = 0) const;

public:
template<typename T>
inline decltype(auto) get(const table_t* table = nullptr) const;
template<typename T_dataset>
inline decltype(auto) get() const;

inline data_context(
const schema_t& p_schema,
::cppmariadb::connection& p_connection)
: base_context (p_schema, p_connection)
, _dataset_id (0)
, _dataset (nullptr)
, _table (nullptr)
{ }

template<typename T_data>
inline data_context(
@@ -81,8 +94,9 @@ beg_namespace_cpphibernate_driver_mariadb
const schema_t& p_schema,
::cppmariadb::connection& p_connection)
: base_context (p_schema, p_connection)
, data_id (misc::get_type_id(hana::type_c<mp::decay_t<T_data>>))
, data (&p_data)
, _dataset_id (misc::get_type_id(hana::type_c<mp::decay_t<T_data>>))
, _dataset (&p_data)
, _table (nullptr)
{ }
};

@@ -93,6 +107,14 @@ beg_namespace_cpphibernate_driver_mariadb
{
const filter_t& filter;

inline filter_context(
const schema_t& p_schema,
::cppmariadb::connection& p_connection,
const filter_t& p_filter)
: data_context (p_schema, p_connection)
, filter (p_filter)
{ }

template<typename T_data>
inline filter_context(
T_data& p_data,
@@ -135,40 +157,40 @@ beg_namespace_cpphibernate_driver_mariadb
struct read_context
: public filter_context
{
bool is_dynamic;
protected:
size_t base_dataset_id;

public:
bool is_dynamic;
std::string where;
std::string limit;
std::string order_by;

template<typename T_data>
protected:
inline read_context(
T_data& p_data,
const schema_t& p_schema,
::cppmariadb::connection& p_connection,
const filter_t& p_filter)
: filter_context (p_data, p_schema, p_connection, p_filter)
: filter_context (p_schema, p_connection, p_filter)
, is_dynamic (false)
, base_dataset_id (0)
{ }

public:
virtual ~read_context() = default;

template<typename T_dataset>
inline T_dataset& emplace(const table_t* table = nullptr) const;

void emplace() const
{ emplace_intern(nullptr); }
{ emplace_intern(nullptr, 0); }

void finish() const
{ finish_intern(); }

private:
virtual void* emplace_intern(void* data) const
{ throw misc::hibernate_exception("read_context::emplace_intern is not implemented!"); }

virtual void finish_intern() const
{ throw misc::hibernate_exception("read_context::finish_intern is not implemented!"); }
virtual void* emplace_intern(void* data, size_t dataset_id) const = 0;
virtual void finish_intern () const = 0;
};

using read_context_ptr = std::unique_ptr<read_context>;


+ 45
- 21
include/cpphibernate/driver/mariadb/helper/context.inl View File

@@ -7,29 +7,50 @@ beg_namespace_cpphibernate_driver_mariadb

/* data_context */

template<typename T>
template<typename T_dataset>
inline void* data_context
::set(T_dataset& dataset, size_t dataset_id) const
{
using dataset_type = mp::decay_t<T_dataset>;

_table = nullptr;
_dataset = &dataset;
_dataset_id = (dataset_id == 0)
? misc::get_type_id(hana::type_c<dataset_type>)
: dataset_id;

return _dataset;
}

template<typename T_dataset>
inline decltype(auto) data_context
::get(const table_t* table) const
::get() const
{
if (!data)
using dataset_type = mp::decay_t<T_dataset>;

if (!_dataset)
throw misc::hibernate_exception("no data assigned!");

auto type_id = misc::get_type_id(hana::type_c<mp::decay_t<T>>);
if (type_id != data_id)
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>);
if (dataset_id != _dataset_id)
{
if (!table)
table = &schema.table(type_id);
/* check table */
if (!_table)
_table = &schema.table(_dataset_id);
else if (_table->dataset_id != _dataset_id)
throw misc::hibernate_exception("invalid table!");

while(table && table->dataset_id != type_id)
auto table = _table;
while(table && table->dataset_id != dataset_id)
table = table->base_table;

if (!table)
{
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { }
<< "invalid type! expected " << data_id << ", got " << type_id).str());
throw misc::hibernate_exception(utl::type_helper<dataset_type>::name() +
" is not a derived type of dataset with id " + std::to_string(_dataset_id));
}
}
return *static_cast<T*>(data);
return *static_cast<dataset_type*>(_dataset);
}

/* read_context */
@@ -38,29 +59,32 @@ beg_namespace_cpphibernate_driver_mariadb
T_dataset& read_context
::emplace(const table_t* table) const
{
if (!is_dynamic || base_dataset_id == 0)
throw misc::hibernate_exception("dynamic creation is deactivated for this context!");
using dataset_type = mp::decay_t<T_dataset>;

// check table
auto dataset_id = misc::get_type_id(hana::type_c<mp::decay_t<T_dataset>>);
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>);
if (!table)
table = &schema.table(dataset_id);
else if (table->dataset_id != dataset_id)
throw misc::hibernate_exception("dataset id of table and dataset to insert defer!");
throw misc::hibernate_exception("wrong table passed!");

// check base
auto tbl = table;
while (tbl && tbl->dataset_id != base_dataset_id)
tbl = tbl->base_table;
if (!tbl)
throw misc::hibernate_exception(utl::type_helper<T_dataset>::name() + " is not a derived type of table " + table->table_name);
{
throw misc::hibernate_exception(utl::type_helper<dataset_type>::name() +
" is not a derived type of dataset with id " + std::to_string(base_dataset_id));
}

// create dataset
auto ptr = std::make_unique<T_dataset>();
auto ret = emplace_intern(ptr.get());
if (!ret)
ret = ptr.release();
return *static_cast<T_dataset*>(ret);
auto ptr = std::make_unique<dataset_type>();
auto data = emplace_intern(ptr.get(), dataset_id);
if (!data)
throw misc::hibernate_exception("unable to store created dataset in context!");
ptr.release();
return *static_cast<dataset_type*>(data);
}

}

+ 204
- 61
include/cpphibernate/driver/mariadb/impl/read.h View File

@@ -9,85 +9,228 @@
beg_namespace_cpphibernate_driver_mariadb
{

/* read_impl_t */

template<typename T_dataset, typename = void>
struct read_impl_t
namespace __impl
{
using dataset_type = T_dataset;

struct context_impl
: public read_context
template<typename T, typename = void>
struct make_read_context_impl
{
mutable size_t count;
template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&... args)
{ static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_read_context(...)!"); }
};

template<typename T_read_context>
context_impl(T_read_context&& p_read_context)
: read_context (std::forward<T_read_context>(p_read_context))
, count (0)
{ }
template<typename T_dataset, typename... T_args>
struct make_read_context_impl<
mp::list<T_dataset, T_args...>,
mp::enable_if_c<
!misc::is_container<mp::decay_t<T_dataset>>::value
&& !misc::is_nullable<mp::decay_t<T_dataset>>::value>>
{
using dataset_type = mp::decay_t<T_dataset>;

private:
virtual void* emplace_intern(void* data) const override
struct context_impl
: public read_context
{
if (data)
throw misc::hibernate_exception("This context has a pre assigned dataset and can therefor not work in dynamic mode!");
++count;
if (count > 1)
throw misc::hibernate_exception("Expected exactly one dataset, but received more!");
return nullptr;
}

virtual void finish_intern() const override
{
if (count < 1)
throw misc::hibernate_exception("Expected exactly one dataset, but received none!");
}
private:
mutable size_t _count;
dataset_type& _dataset;

public:
template<typename... X_args>
context_impl(dataset_type& dataset, X_args&&... args)
: read_context (std::forward<X_args>(args)...)
, _count (0)
, _dataset (dataset)
{
is_dynamic = false;
base_dataset_id = misc::get_type_id(hana::type_c<dataset_type>);
}

private:
virtual void* emplace_intern(void* data, size_t dataset_id) const override
{
if (data || dataset_id != 0)
throw misc::hibernate_exception("Static datasets can not be assigned!");
++_count;
if (_count > 1)
throw misc::hibernate_exception("Expected exactly one dataset, but received more!");
return set(_dataset);
}

virtual void finish_intern() const override
{
if (_count < 1)
throw misc::hibernate_exception("Expected exactly one dataset, but received none!");
}
};

static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args)
{ return context_impl(dataset, std::forward<T_args>(args)...); }
};

static inline void apply(const read_context& context)
template<typename T_dataset, typename... T_args>
struct make_read_context_impl<
mp::list<T_dataset, T_args...>,
mp::enable_if_c<
!misc::is_container<mp::decay_t<T_dataset>>::value
&& misc::is_nullable<mp::decay_t<T_dataset>>::value>>
{
auto dataset_id = misc::get_type_id(hana::type_c<dataset_type>);
auto& connection = context.connection;
auto& schema = context.schema;
auto& table = schema.table(dataset_id);

transaction_lock trans(connection);
table.read(context_impl(context));
trans.commit();
}
};

/* read_impl_t - nullable */

template<typename T_dataset>
struct read_impl_t<
T_dataset,
mp::enable_if<misc::is_nullable<T_dataset>>>
{
using dataset_type = T_dataset;
using nullable_helper_type = misc::nullable_helper<dataset_type>;
using dataset_type = mp::decay_t<T_dataset>;
using nullable_helper_type = misc::nullable_helper<dataset_type>;
using value_type = typename nullable_helper_type::value_type;

static inline void apply(const read_context& context)
struct context_impl
: public read_context
{
private:
dataset_type& _dataset;
mutable size_t _count;

public:
template<typename... X_args>
context_impl(dataset_type& dataset, X_args&&... args)
: read_context (std::forward<X_args>(args)...)
, _dataset (dataset)
, _count (0)
{
is_dynamic = misc::is_pointer<dataset_type>::value;
base_dataset_id = misc::get_type_id(hana::type_c<value_type>);
nullable_helper_type::clear(_dataset);
}

private:
virtual void* emplace_intern(void* data, size_t dataset_id) const override
{
if (data && !misc::is_pointer<dataset_type>::value)
throw misc::hibernate_exception("This is not a pointer type!");
++_count;
if (_count > 1)
throw misc::hibernate_exception("Expected exactly one dataset, but received more!");

if (data)
{
auto* cast = static_cast<value_type*>(data);
auto& value = nullable_helper_type::set(_dataset, cast);
if (cast != &value)
throw misc::hibernate_exception("Nullable pointer value has changed!");
return set(value, dataset_id);
}
else
{
auto& value = nullable_helper_type::set(_dataset, value_type { });
return set(value);
}
}

virtual void finish_intern() const override
{ }
};

static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args)
{ return context_impl(dataset, std::forward<T_args>(args)...); }
};

template<typename T_dataset, typename... T_args>
struct make_read_context_impl<
mp::list<T_dataset, T_args...>,
mp::enable_if_c<
misc::is_container<mp::decay_t<T_dataset>>::value
&& !misc::is_nullable<mp::decay_t<T_dataset>>::value>>
{
using dataset_type = mp::decay_t<T_dataset>;
using real_dataset_type = misc::real_dataset_t<dataset_type>;
using container_helper_type = misc::container_helper<dataset_type>;
using value_type = typename container_helper_type::value_type;

struct context_impl
: public read_context
{
private:
dataset_type& _dataset;
mutable size_t _count;

public:
template<typename... X_args>
context_impl(dataset_type& dataset, X_args&&... args)
: read_context (std::forward<X_args>(args)...)
, _dataset (dataset)
, _count (0)
{
is_dynamic = misc::is_pointer<value_type>::value;
base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>);
container_helper_type::clear(_dataset);
}

private:
virtual void* emplace_intern(void* data, size_t dataset_id) const override
{
if (data || dataset_id != 0)
throw misc::hibernate_exception("Static datasets can not be assigned!");
auto& value = container_helper_type::emplace(_dataset);
return set(value);
}

virtual void finish_intern() const override
{ }
};

static constexpr decltype(auto) apply(dataset_type& dataset, T_args&&... args)
{ return context_impl(dataset, std::forward<T_args>(args)...); }
};

}
};
}

/* read_impl_t - container */
constexpr decltype(auto) make_read_context = misc::make_generic_predicate<__impl::make_read_context_impl> { };

template<typename T_dataset>
struct read_impl_t<
T_dataset,
mp::enable_if<misc::is_container<T_dataset>>>
namespace __impl
{
using dataset_type = T_dataset;

static inline void apply(const read_context& context)
template<typename T, typename = void>
struct make_fake_context_impl
{
template<typename... T_args>
static constexpr decltype(auto) apply(T_args&&... args)
{ static_assert(sizeof...(args) == -1, "Invalid parameters for mariadb::make_fake_context(...)!"); }
};

template<typename T_wrapped_dataset, typename... T_args>
struct make_fake_context_impl<
mp::list<T_wrapped_dataset, T_args...>,
mp::enable_if_c<
hana::is_a_t<hana::type_tag, T_wrapped_dataset>::value>>
{
using wrapped_dataset_type = mp::decay_t<T_wrapped_dataset>;
using dataset_type = misc::unwrap_t<wrapped_dataset_type>;
using real_dataset_type = misc::real_dataset_t<dataset_type>;

struct context_impl
: public read_context
{
public:
template<typename... X_args>
context_impl(X_args&&... args)
: read_context (std::forward<X_args>(args)...)
{
is_dynamic = misc::is_pointer<dataset_type>::value;
base_dataset_id = misc::get_type_id(hana::type_c<real_dataset_type>);
}

private:
virtual void* emplace_intern(void* data, size_t dataset_id) const override
{ return nullptr; }

virtual void finish_intern() const override
{ }
};

static constexpr decltype(auto) apply(T_wrapped_dataset&&, T_args&&... args)
{ return context_impl(std::forward<T_args>(args)...); }
};

}

}
};
constexpr decltype(auto) make_fake_context = misc::make_generic_predicate<__impl::make_fake_context_impl> { };

}
end_namespace_cpphibernate_driver_mariadb

+ 0
- 1
include/cpphibernate/driver/mariadb/impl/where.h View File

@@ -98,7 +98,6 @@ beg_namespace_cpphibernate_driver_mariadb
},
[]{ });
});
os << ")";
statement.assign(os.str());
}
};


+ 10
- 2
include/cpphibernate/driver/mariadb/mariadb.h View File

@@ -44,11 +44,19 @@ beg_namespace_cpphibernate_driver_mariadb
template<typename T_dataset, typename T_modifiers>
inline void read_impl(T_dataset& dataset, T_modifiers&& modifiers) const
{
read_context context(dataset, _schema, _connection, _filter);
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);
read_impl_t<T_dataset>::apply(context);

transaction_lock trans(_connection);
table.read(context);
trans.commit();
}
};



+ 1
- 1
include/cpphibernate/driver/mariadb/schema/field.fwd.h View File

@@ -6,7 +6,7 @@

beg_namespace_cpphibernate_driver_mariadb
{
/* field_t */

struct field_t;


+ 4
- 2
include/cpphibernate/driver/mariadb/schema/field.h View File

@@ -24,6 +24,7 @@ beg_namespace_cpphibernate_driver_mariadb
size_t real_value_id { 0 }; // unique id of the real/unwrapped value type

bool value_is_nullable { false }; // value is stored in a nullable container
bool value_is_pointer { false }; // value is stored in a pointer container
bool value_is_container { false }; // value is stored in a container
bool value_is_ordered { false }; // value is stored in a ordered container (vector, list, ...)
bool value_is_auto_incremented { false }; // value is a auto incremented field
@@ -53,6 +54,7 @@ beg_namespace_cpphibernate_driver_mariadb
, value_id (std::move(other).value_id)
, real_value_id (std::move(other).real_value_id)
, value_is_nullable (std::move(other).value_is_nullable)
, value_is_pointer (std::move(other).value_is_pointer)
, value_is_container (std::move(other).value_is_container)
, value_is_auto_incremented (std::move(other).value_is_auto_incremented)
, table (nullptr)
@@ -77,7 +79,7 @@ beg_namespace_cpphibernate_driver_mariadb
using read_context_ptr = std::unique_ptr<read_context>;

virtual value_t foreign_create_update (const create_update_context& context) const;
virtual read_context_ptr foreign_read (const read_context& context, const value_t& value) const;
virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const;

/* properties */
virtual value_t get (const data_context& context) const;
@@ -180,7 +182,7 @@ beg_namespace_cpphibernate_driver_mariadb

public:
virtual value_t foreign_create_update(const create_update_context& context) const override;
virtual read_context_ptr foreign_read (const read_context& context, const value_t& value) const override;
virtual read_context_ptr foreign_read (const read_context& context, bool fake_context) const override;
};

}

+ 18
- 33
include/cpphibernate/driver/mariadb/schema/field.inl View File

@@ -21,6 +21,7 @@ beg_namespace_cpphibernate_driver_mariadb
real_value_id = misc::get_type_id(hana::type_c<real_value_type>);

value_is_nullable = misc::is_nullable<value_type>::value;
value_is_pointer = misc::is_pointer<value_type>::value;
value_is_container = misc::is_container<value_type>::value;
value_is_ordered = misc::is_ordered<value_type>::value;
}
@@ -39,7 +40,7 @@ beg_namespace_cpphibernate_driver_mariadb
value_t value_field_t<T_field>
::get(const data_context& context) const
{
auto& dataset = context.get<dataset_type>(this->table);
auto& dataset = context.get<dataset_type>();
return type_props::convert_from(this->field.getter(dataset));
}

@@ -47,7 +48,7 @@ beg_namespace_cpphibernate_driver_mariadb
void value_field_t<T_field>
::set(const data_context& context, const value_t& value) const
{
auto& dataset = context.get<dataset_type>(this->table);
auto& dataset = context.get<dataset_type>();
this->field.setter(dataset, type_props::convert_to(value));
}

@@ -89,38 +90,22 @@ beg_namespace_cpphibernate_driver_mariadb

template<typename T_field>
read_context_ptr foreign_table_field_t<T_field>
::foreign_read(const read_context& context, const value_t& value) const
::foreign_read(const read_context& context, bool fake_context) const
{
using is_nullable_type = misc::is_nullable<value_type>;
return hana::eval_if(
is_nullable_type { },
[&](auto _) {
auto& dataset = _(context).template get<dataset_type>();
auto& member = this->field.getter(dataset);
using nullable_helper_type = misc::nullable_helper<mp::decay_t<decltype(member)>>;
if (value.has_value())
{
auto& new_dataset = nullable_helper_type::set(member, real_value_type { });
auto new_context = change_context(context, new_dataset);
return std::make_unique<read_context>(new_context);
}
else
{
nullable_helper_type::clear(member);
return read_context_ptr { };
}
},
[&](auto _) {
if (!value.has_value())
{
throw misc::hibernate_exception(std::string("excepted value for field ") +
this->table_name + "." + this->field_name + " but received null!");
}
auto& dataset = context.get<dataset_type>();
auto& member = this->field.getter(dataset);
auto new_context = change_context(context, member);
return std::make_unique<read_context>(new_context);
});
if (!fake_context)
{
auto& dataset = context.get<dataset_type>();
auto& member = this->field.getter(dataset);
auto new_context = make_read_context(member, context.schema, context.connection, context.filter);
using context_type = mp::decay_t<decltype(new_context)>;
return std::make_unique<context_type>(new_context);
}
else
{
auto new_context = make_fake_context(hana::type_c<value_type>, context.schema, context.connection, context.filter);
using context_type = mp::decay_t<decltype(new_context)>;
return std::make_unique<context_type>(new_context);
}
}

namespace __impl


+ 1
- 1
include/cpphibernate/driver/mariadb/schema/table.fwd.h View File

@@ -6,7 +6,7 @@

beg_namespace_cpphibernate_driver_mariadb
{
/* table_t */

struct table_t;


+ 7
- 1
include/cpphibernate/driver/mariadb/schema/table.h View File

@@ -64,7 +64,10 @@ beg_namespace_cpphibernate_driver_mariadb

void print(std::ostream& os) const;

const table_t* get_derived(size_t id) const;
const table_t* get_derived_by_table_id(size_t id) const;
const table_t* get_derived_by_dataset_id(size_t id) const;

virtual void emplace(const read_context& context) const;

/* CRUD */
inline void init_stage1(const init_context& context) const
@@ -158,9 +161,12 @@ beg_namespace_cpphibernate_driver_mariadb
using table_type = typename base_type::table_type;
using base_dataset_type = typename base_type::base_dataset_type;
using dataset_type = typename table_type::dataset_type;
using real_dataset_type = misc::real_dataset_t<dataset_type>;

using base_type::base_type;

virtual void emplace(const read_context& context) const override;

private:
template<typename T_dataset, typename T_pred, typename T_include_self>
constexpr void for_each_derived(T_dataset& dataset, const T_include_self& include_self, const T_pred& pred) const;


+ 7
- 2
include/cpphibernate/driver/mariadb/schema/table.inl View File

@@ -8,6 +8,11 @@ beg_namespace_cpphibernate_driver_mariadb

/* table_polymorphic_t */

template<typename T_schema, typename T_table, typename T_base_dataset>
void table_polymorphic_t<T_schema, T_table, T_base_dataset>
::emplace(const read_context& context) const
{ context.emplace<real_dataset_type>(this); }

template<typename T_schema, typename T_table, typename T_base_dataset>
template<typename T_dataset, typename T_pred, typename T_include_self>
constexpr void table_polymorphic_t<T_schema, T_table, T_base_dataset>
@@ -42,7 +47,7 @@ beg_namespace_cpphibernate_driver_mariadb
{
using derived_dataset_type = mp::decay_t<decltype(derived_dataset)>;
auto derived_dataset_id = misc::get_type_id(hana::type_c<derived_dataset_type>);
auto derived_table = this->get_derived(derived_dataset_id);
auto derived_table = this->get_derived_by_dataset_id(derived_dataset_id);
if (!derived_table)
{
throw misc::hibernate_exception(static_cast<std::ostringstream&>(std::ostringstream { }
@@ -71,7 +76,7 @@ beg_namespace_cpphibernate_driver_mariadb
},
[this, &context](auto _)->std::string {
using tmp_type = misc::decay_unwrap_t<decltype(_(hana::type_c<base_dataset_type>))>;
assert(base_table);
assert(this->base_table);
auto& dataset = context.get<dataset_type>();
auto& base = static_cast<tmp_type&>(dataset);
return this->base_table->create_update_exec(change_context(context, base));


+ 1
- 0
include/cpphibernate/misc.h View File

@@ -1,5 +1,6 @@
#pragma once

#include <cpphibernate/misc/container_helper.h>
#include <cpphibernate/misc/general.h>
#include <cpphibernate/misc/meta.h>
#include <cpphibernate/misc/nullable_helper.h>


+ 2
- 0
include/cpphibernate/misc/general.h View File

@@ -1,5 +1,7 @@
#pragma once

#include <iostream> // TODO debug!

#include <cpphibernate/config.h>
#include <cpputils/misc/type_helper.h>



+ 19
- 4
include/cpphibernate/misc/nullable_helper.h View File

@@ -37,6 +37,9 @@ beg_namespace_cpphibernate_misc
static inline const value_type* get(const nullable_type& x)
{ return x.has_value() ? &x.value() : nullptr; }

static inline value_type& set(nullable_type& x, const value_type* value)
{ return *(x = *value); }

static inline value_type& set(nullable_type& x, const value_type& value)
{ return *(x = value); }

@@ -58,9 +61,9 @@ beg_namespace_cpphibernate_misc
static inline value_type* get(const nullable_type& x)
{ return x.get(); }

static inline value_type& set(nullable_type& x, value_type&& value)
static inline value_type& set(nullable_type& x, value_type* value)
{
x.reset(new value_type(std::move(value)));
x.reset(value);
return *x;
}

@@ -70,6 +73,12 @@ beg_namespace_cpphibernate_misc
return *x;
}

static inline value_type& set(nullable_type& x, value_type&& value)
{
x.reset(new value_type(std::move(value)));
return *x;
}

static void clear(nullable_type& x)
{ return x.reset(); }
};
@@ -85,9 +94,9 @@ beg_namespace_cpphibernate_misc
static inline value_type* get(const nullable_type& x)
{ return x.get(); }

static inline value_type& set(nullable_type& x, value_type&& value)
static inline value_type& set(nullable_type& x, value_type* value)
{
x.reset(new value_type(std::move(value)));
x.reset(value);
return *x;
}

@@ -97,6 +106,12 @@ beg_namespace_cpphibernate_misc
return *x;
}

static inline value_type& set(nullable_type& x, value_type&& value)
{
x.reset(new value_type(std::move(value)));
return *x;
}

static void clear(nullable_type& x)
{ return x.reset(); }
};


+ 2
- 1
src/driver/mariadb/schema/field.cpp View File

@@ -24,6 +24,7 @@ void field_t::print(std::ostream& os) const
<< indent << "\"value_id\": " << value_id << ","
<< indent << "\"real_value_id\": " << real_value_id << ","
<< indent << "\"value_is_nullable\": " << (value_is_nullable ? "true" : "false") << ","
<< indent << "\"value_is_pointer\": " << (value_is_pointer ? "true" : "false") << ","
<< indent << "\"value_is_container\": " << (value_is_container ? "true" : "false") << ","
<< indent << "\"value_is_auto_incremented\": " << (value_is_auto_incremented ? "true" : "false") << ","
<< indent << "\"table\": " << (table ? std::string("\"") + table->table_name + "\"" : "null") << ","
@@ -150,7 +151,7 @@ void field_t::update()
/* CRUD */

throw_not_implemented(value_t, foreign_create_update, const create_update_context&)
throw_not_implemented(read_context_ptr, foreign_read, const read_context&, const value_t&)
throw_not_implemented(read_context_ptr, foreign_read, const read_context&, bool fake_context)

/* properties */



+ 325
- 87
src/driver/mariadb/schema/table.cpp View File

@@ -15,11 +15,23 @@ using namespace ::cpphibernate::driver::mariadb_impl;

/* data_extractor_t */

struct foreign_many_tuple_t
{
const field_t& field;
read_context_ptr context;
std::string owner;
};

struct foreign_many_list_t :
public std::list<foreign_many_tuple_t>
{ };

struct data_extractor_t
{
const table_t& _table;
const read_context& _context;
const ::cppmariadb::row& _row;
foreign_many_list_t& _foreign_many_list;

const filter_t& _filter;
mutable size_t _index;
@@ -27,14 +39,19 @@ struct data_extractor_t
data_extractor_t(
const table_t& p_table,
const read_context& p_context,
const ::cppmariadb::row& p_row)
: _table (p_table)
, _context (p_context)
, _row (p_row)
, _filter (p_context.filter)
, _index (0)
const ::cppmariadb::row& p_row,
foreign_many_list_t& p_foreign_many_list)
: _table (p_table)
, _context (p_context)
, _row (p_row)
, _foreign_many_list(p_foreign_many_list)
, _filter (p_context.filter)
, _index (0)
{ }

inline bool has_value() const
{ return !_row.at(_index).is_null(); }

inline value_t get_value() const
{
value_t ret;
@@ -44,26 +61,67 @@ struct data_extractor_t
return ret;
}

inline void read_field(const field_t& field, const read_context* context) const
inline void next_field() const
{ ++_index; }

inline void read_field(const field_t& field, const read_context& context, bool skip = false) const
{
if (context)
{
field.set(*context, get_value());
}
auto value = get_value();
++_index;
if (!skip)
field.set(context, value);
}

inline void read_table(const table_t& table, const read_context* context) const
inline bool read_table(const table_t& table, const read_context& context, bool read_base, bool read_derived, bool skip = false) const
{
if (table.base_table)
read_table(*table.base_table, context);
/* read the base table */
if (read_base && table.base_table)
{
skip = read_table(*table.base_table, context, true, false, skip);
}

/* create a dynamic dataset depending on the derived table */
else if ( read_base
&& context.is_dynamic
&& !table.derived_tables.empty())
{
auto value = get_value();
next_field();
if (static_cast<bool>(value) && !skip)
{
auto type = utl::from_string<uint>(*value);
auto derived = _table.get_derived_by_table_id(type);
if (!derived)
throw misc::hibernate_exception(std::string("unable to find dereived table for id ") + std::to_string(type));
derived->emplace(context);
}
else
{
skip = true;
}
}

/* create a static dataset */
else if (has_value() && !skip)
{
if (read_base)
{
context.emplace();
}
}

/* no data -> skip */
else
{
skip = true;
}

if (_context.filter.is_excluded(table))
return;
return skip;

/* primary key */
assert(table.primary_key_field);
read_field(*table.primary_key_field, context);
read_field(*table.primary_key_field, context, skip);

/* data fields */
for (auto& ptr : table.data_fields)
@@ -71,7 +129,7 @@ struct data_extractor_t
assert(ptr);
auto& field = *ptr;
if (!_context.filter.is_excluded(field))
read_field(field, context);
read_field(field, context, skip);
}

/* foreign table one */
@@ -87,19 +145,54 @@ struct data_extractor_t
|| _filter.is_excluded(ref_table))
continue;

read_context_ptr next_context;
if (context)
next_context = field.foreign_read(*context, get_value());
auto next_context = field.foreign_read(context, skip);
assert(static_cast<bool>(next_context));
read_table(ref_table, *next_context, true, true, skip);
next_context->finish();
}

read_table(ref_table, next_context.get());
/* foreign table many */
if (!skip)
{
for (auto& ptr : table.foreign_table_many_fields)
{
assert(ptr);
assert(ptr->referenced_table);

auto& field = *ptr;
auto& ref_table = *field.referenced_table;

if ( _filter.is_excluded(field)
|| _filter.is_excluded(ref_table))
continue;

_foreign_many_list.emplace_back(
foreign_many_tuple_t {
field,
field.foreign_read(context, false),
*table.primary_key_field->get(context),
});
}
}

/* derived tables */
if (read_derived && context.is_dynamic)
{
for (auto& ptr : table.derived_tables)
{
assert(ptr);
auto& derived_table = *ptr;
read_table(derived_table, context, false, true, skip);
}
}

return skip;
}

inline void operator()() const
{
_index = 0;
_context.emplace();
read_table(_table, &_context);
read_table(_table, _context, true, true, false);
if (_index != _row.size())
throw misc::hibernate_exception("result was not completely read!");
}
@@ -109,14 +202,23 @@ struct data_extractor_t

struct select_query_builder_t
{
const table_t& _table;
const filter_t& _filter;
bool _is_dynamic;

size_t alias_id { 0 };
size_t index { 0 };
std::ostringstream os;
std::ostringstream join;
struct local_context
{
const table_t& table;
std::string alias;
bool add_base;
bool add_derived;
bool is_dynamic;
};

const table_t& _table;
const filter_t& _filter;
bool _is_dynamic;

size_t alias_id { 0 };
size_t index { 0 };
std::ostringstream os;
std::list<std::string> joins;

select_query_builder_t(
const table_t& p_table,
@@ -127,7 +229,7 @@ struct select_query_builder_t
, _is_dynamic(p_is_dynamic)
{ }

inline std::string make_alias(const table_t& table)
inline std::string make_alias()
{ return std::string("T") + std::to_string(alias_id++); }

inline void add_field(const field_t& field, const std::string& alias)
@@ -142,68 +244,87 @@ struct select_query_builder_t
<< field.convert_from_close;
}

inline bool add_table(const table_t& table, const std::string& alias)
inline bool add_table(const local_context& ctx)
{
bool ret = false;
bool ret = false;
auto has_alias = !ctx.alias.empty();
auto real_alias = has_alias ? ctx.alias : ctx.table.table_name;

if (table.base_table)
if (ctx.table.base_table && ctx.add_base)
{
auto& base_table = *table.base_table;
auto base_alias = make_alias(base_table);
auto tmp = add_table(base_table, base_alias);
if (tmp)
assert(ctx.table.base_table->primary_key_field);
auto& base_table = *ctx.table.base_table;
auto& base_key = *base_table.primary_key_field;
auto base_alias = has_alias ? make_alias() : std::string();
auto real_base_alias = has_alias ? base_alias : base_table.table_name;

std::ostringstream ss;
ss << " JOIN `"
<< base_table.table_name;
if (has_alias)
{
ss << "` AS `"
<< base_alias;
}
ss << "` ON `"
<< real_alias
<< "`.`"
<< base_key.field_name
<< "`=`"
<< real_base_alias
<< "`.`"
<< base_key.field_name
<< "`";

auto it = joins.insert(joins.end(), ss.str());
if (add_table({
base_table,
base_alias,
true,
false,
ctx.is_dynamic
}))
{
assert(base_table.primary_key_field);
auto& base_key = *base_table.primary_key_field;
ret = true;
join << " LEFT JOIN `"
<< base_table.table_name
<< "` AS `"
<< base_alias
<< "` ON `"
<< alias
<< "`.`"
<< base_key.field_name
<< "`=`"
<< base_alias
<< "`.`"
<< base_key.field_name
<< "`";
}
else
{
joins.erase(it);
}
}

/* __type */
if ( _is_dynamic
&& !table.base_table
&& !table.derived_tables.empty())
if ( ctx.is_dynamic
&& !ctx.table.base_table
&& !ctx.table.derived_tables.empty())
{
if (index++) os << ", ";
os << "`"
<< table.table_name
<< ctx.table.table_name
<< "`.`__type` AS `__type`";
ret = true;
}

if (_filter.is_excluded(table))
if (_filter.is_excluded(ctx.table))
return ret;

ret = true;

/* primary key */
assert(table.primary_key_field);
add_field(*table.primary_key_field, alias);
assert(ctx.table.primary_key_field);
add_field(*ctx.table.primary_key_field, real_alias);

/* data fields */
for (auto& ptr : table.data_fields)
for (auto& ptr : ctx.table.data_fields)
{
assert(ptr);
auto& field = *ptr;
if (!_filter.is_excluded(field))
add_field(field, alias);
add_field(field, real_alias);
}

/* foreign table one */
for (auto& ptr : table.foreign_table_one_fields)
for (auto& ptr : ctx.table.foreign_table_one_fields)
{
assert(ptr);
assert(ptr->referenced_table);
@@ -217,23 +338,84 @@ struct select_query_builder_t
|| _filter.is_excluded(ref_table))
continue;

auto new_alias = make_alias(ref_table);
add_table(ref_table, new_alias);
join << " LEFT JOIN `"
<< ref_table.table_name
<< "` AS `"
<< new_alias
<< "` ON `"
<< alias
auto new_alias = make_alias();

std::ostringstream ss;
ss << " LEFT JOIN `"
<< ref_table.table_name
<< "` AS `"
<< new_alias
<< "` ON `"
<< real_alias
<< "`.`"
<< ref_key.table_name
<< "_id_"
<< field.field_name
<< "`=`"
<< new_alias
<< "`.`"
<< ref_key.field_name
<< "`";

auto it = joins.insert(joins.end(), ss.str());
if (!add_table({
ref_table,
new_alias,
true,
true,
field.value_is_pointer
}))
{
joins.erase(it);
}
}

/* derived tables */
if (ctx.add_derived && ctx.is_dynamic)
{
for (auto& ptr : ctx.table.derived_tables)
{
assert(ptr);
assert(ptr->primary_key_field);
auto& derived_table = *ptr;
auto& primary_key = *ctx.table.primary_key_field;
auto derived_alias = has_alias ? make_alias() : std::string();
auto real_derived_alias = has_alias ? derived_alias : derived_table.table_name;

std::ostringstream ss;
ss << " LEFT JOIN `"
<< derived_table.table_name;
if (has_alias)
{
ss << "` AS `"
<< derived_alias;
}
ss << "` ON `"
<< real_alias
<< "`.`"
<< ref_key.table_name
<< "_id_"
<< field.field_name
<< primary_key.field_name
<< "`=`"
<< new_alias
<< real_derived_alias
<< "`.`"
<< ref_key.field_name
<< primary_key.field_name
<< "`";

auto it = joins.insert(joins.end(), ss.str());
if (add_table({
derived_table,
derived_alias,
false,
true,
ctx.is_dynamic,
}))
{
ret = true;
}
else
{
joins.erase(it);
}
}
}

return ret;
@@ -242,15 +424,21 @@ struct select_query_builder_t
inline std::string operator()()
{
os << "SELECT ";
auto alias = make_alias(_table);
add_table(_table, alias);
add_table({
_table,
"",
true,
true,
_is_dynamic,
});
os << " FROM `"
<< _table.table_name
<< "` AS `"
<< alias
<< "`"
<< join.str()
<< " ?where! ?order! ?limit!";
<< "`";

for (auto& join : joins)
os << join;

os << " ?where! ?order! ?limit!";
return os.str();
}
};
@@ -958,19 +1146,35 @@ void table_t::print(std::ostream& os) const
<< indent << '}';
}

const table_t* table_t::get_derived(size_t id) const
const table_t* table_t::get_derived_by_table_id(size_t id) const
{
if (table_id == id)
return this;
for (auto ptr : derived_tables)
{
assert(ptr);
auto ret = ptr->get_derived_by_table_id(id);
if (ret) return ret;
}
return nullptr;
}

const table_t* table_t::get_derived_by_dataset_id(size_t id) const
{
if (dataset_id == id)
return this;
for (auto ptr : derived_tables)
{
assert(ptr);
auto ret = ptr->get_derived(id);
auto ret = ptr->get_derived_by_dataset_id(id);
if (ret) return ret;
}
return nullptr;
}

void table_t::emplace(const read_context& context) const
{ throw misc::hibernate_exception(std::string("'") + table_name + "' does not implement the emplace() method!"); }

::cppmariadb::statement& table_t::get_statement_create_table() const
{
if (_statement_create_table)
@@ -1051,13 +1255,47 @@ void table_t::read_exec(const read_context& context) const
{
auto& statement = get_statement_select(context);
auto& connection = context.connection;

statement.set(0, context.where);
statement.set(1, context.order_by);
statement.set(2, context.limit);

cpphibernate_debug_log("execute SELECT query: " << statement.query(connection));
auto result = connection.execute_used(statement);
if (!result)
throw misc::hibernate_exception("Unable to fetching data from database!");

::cppmariadb::row* row;
foreign_many_list_t foreign_many_list;
while ((row = result->next()))
data_extractor_t(*this, context, *row)();
data_extractor_t(*this, context, *row, foreign_many_list)();
context.finish();

for (auto& tuple : foreign_many_list)
{
auto& field = tuple.field;
auto& next_context = *tuple.context;
assert(field.table);
assert(field.referenced_table);
assert(field.referenced_table->primary_key_field);
auto& ref_table = *field.referenced_table;
auto& ref_field = *ref_table.primary_key_field;

std::ostringstream ss;
ss << "WHERE (`"
<< ref_table.table_name
<< "`.`"
<< field.table_name
<< "_id_"
<< field.field_name
<< "`="
<< ref_field.convert_to_open
<< "'"
<< context.connection.escape(tuple.owner)
<< "'"
<< ref_field.convert_to_close
<< ")";
next_context.where = ss.str();
ref_table.read(next_context);
}
}

+ 375
- 53
test/cpphibernate_read.cpp View File

@@ -15,14 +15,16 @@ TEST(CppHibernateTests, read_test1)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T0`.`tbl_test1_id`), "
"`T0`.`str_data`, "
"`T0`.`str64_data`, "
"`T0`.`u32_nullable`, "
"`T0`.`u32_ptr_u`, "
"`T0`.`u32_ptr_s` "
"BinToUuid(`tbl_test1`.`tbl_test1_id`), "
"`tbl_test1`.`str_data`, "
"`tbl_test1`.`str64_data`, "
"`tbl_test1`.`u32_nullable`, "
"`tbl_test1`.`u32_ptr_u`, "
"`tbl_test1`.`u32_ptr_s` "
"FROM "
"`tbl_test1` AS `T0` ",
"`tbl_test1` "
"WHERE "
"(`tbl_test1`.`tbl_test1_id`=UuidToBin('X3d12697a-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" }
}));
@@ -64,13 +66,15 @@ TEST(CppHibernateTests, read_test2)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T0`.`tbl_test2_id`), "
"`T0`.`u8_data`, "
"`T0`.`i8_data`, "
"`T0`.`u16_data`, "
"`T0`.`i16_data` "
"BinToUuid(`tbl_test2`.`tbl_test2_id`), "
"`tbl_test2`.`u8_data`, "
"`tbl_test2`.`i8_data`, "
"`tbl_test2`.`u16_data`, "
"`tbl_test2`.`i16_data` "
"FROM "
"`tbl_test2` AS `T0` ",
"`tbl_test2` "
"WHERE "
"(`tbl_test2`.`tbl_test2_id`=UuidToBin('X3d1270dc-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" }
}));
@@ -108,13 +112,15 @@ TEST(CppHibernateTests, read_test3)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T0`.`tbl_test3_id`), "
"`T0`.`u32_data`, "
"`T0`.`i32_data`, "
"`T0`.`u64_data`, "
"`T0`.`i64_data` "
"BinToUuid(`tbl_test3`.`tbl_test3_id`), "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"FROM "
"`tbl_test3` AS `T0` ",
"`tbl_test3` "
"WHERE "
"(`tbl_test3`.`tbl_test3_id`=UuidToBin('X3d12737a-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" }
}));
@@ -152,22 +158,24 @@ TEST(CppHibernateTests, read_derived1_static)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T1`.`tbl_base_id`), "
"`T1`.`name`, "
"BinToUuid(`T0`.`tbl_derived1_id`), "
"`T0`.`enum_data`, "
"BinToUuid(`T2`.`tbl_test1_id`), "
"`T2`.`str_data`, "
"`T2`.`str64_data`, "
"`T2`.`u32_nullable`, "
"`T2`.`u32_ptr_u`, "
"`T2`.`u32_ptr_s` "
"BinToUuid(`tbl_base`.`tbl_base_id`), "
"`tbl_base`.`name`, "
"BinToUuid(`tbl_derived1`.`tbl_derived1_id`), "
"`tbl_derived1`.`enum_data`, "
"BinToUuid(`T0`.`tbl_test1_id`), "
"`T0`.`str_data`, "
"`T0`.`str64_data`, "
"`T0`.`u32_nullable`, "
"`T0`.`u32_ptr_u`, "
"`T0`.`u32_ptr_s` "
"FROM "
"`tbl_derived1` AS `T0` "
"LEFT JOIN "
"`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` "
"`tbl_derived1` "
"JOIN "
"`tbl_base` ON `tbl_derived1`.`tbl_base_id`=`tbl_base`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_test1` AS `T2` ON `T0`.`tbl_test1_id_test1_data`=`T2`.`tbl_test1_id` ",
"`tbl_test1` AS `T0` ON `tbl_derived1`.`tbl_test1_id_test1_data`=`T0`.`tbl_test1_id` "
"WHERE "
"(`tbl_derived1`.`tbl_derived1_id`=UuidToBin('X3d12758c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d12778a-abb9-11e8-98d0-529269fb1459", "derived1", "3d12758c-abb9-11e8-98d0-529269fb1459", "test2", "3d127988-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `d1.test1_data`", "str64_data of class `test1` object `d1.test1_data`", "32", nullptr, "789" }
}));
@@ -211,34 +219,36 @@ TEST(CppHibernateTests, read_derived2_static)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T1`.`tbl_base_id`), "
"`T1`.`name`, "
"BinToUuid(`T0`.`tbl_derived2_id`), "
"BinToUuid(`tbl_base`.`tbl_base_id`), "
"`tbl_base`.`name`, "
"BinToUuid(`tbl_derived2`.`tbl_derived2_id`), "
"BinToUuid(`T0`.`tbl_test2_id`), "
"`T0`.`u8_data`, "
"`T0`.`i8_data`, "
"`T0`.`u16_data`, "
"`T0`.`i16_data`, "
"BinToUuid(`T1`.`tbl_test2_id`), "
"`T1`.`u8_data`, "
"`T1`.`i8_data`, "
"`T1`.`u16_data`, "
"`T1`.`i16_data`, "
"BinToUuid(`T2`.`tbl_test2_id`), "
"`T2`.`u8_data`, "
"`T2`.`i8_data`, "
"`T2`.`u16_data`, "
"`T2`.`i16_data`, "
"BinToUuid(`T3`.`tbl_test2_id`), "
"`T3`.`u8_data`, "
"`T3`.`i8_data`, "
"`T3`.`u16_data`, "
"`T3`.`i16_data`, "
"BinToUuid(`T4`.`tbl_test2_id`), "
"`T4`.`u8_data`, "
"`T4`.`i8_data`, "
"`T4`.`u16_data`, "
"`T4`.`i16_data` "
"`T2`.`i16_data` "
"FROM "
"`tbl_derived2` AS `T0` "
"`tbl_derived2` "
"JOIN "
"`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` "
"`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T2` ON `T0`.`tbl_test2_id_test2_nullable`=`T2`.`tbl_test2_id` "
"`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T3` ON `T0`.`tbl_test2_id_test2_ptr_u`=`T3`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T4` ON `T0`.`tbl_test2_id_test2_ptr_s`=`T4`.`tbl_test2_id` ",
"`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` "
"WHERE "
"(`tbl_derived2`.`tbl_derived2_id`=UuidToBin('X3d127bcc-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d127db6-abb9-11e8-98d0-529269fb1459", "derived2", "3d127bcc-abb9-11e8-98d0-529269fb1459", "3d1283a6-abb9-11e8-98d0-529269fb1459", "10", "11", "12", "13", "3d128522-abb9-11e8-98d0-529269fb1459", "20", "21", "22", "23", nullptr, nullptr, nullptr, nullptr, nullptr }
}));
@@ -278,3 +288,315 @@ TEST(CppHibernateTests, read_derived2_static)
EXPECT_EQ (d2.test2_ptr_u->i16_data, 23);
EXPECT_FALSE(static_cast<bool>(d2.test2_ptr_s));
}

TEST(CppHibernateTests, read_derived3_static)
{
StrictMock<mariadb_mock> mock;

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`tbl_base`.`tbl_base_id`), "
"`tbl_base`.`name`, "
"BinToUuid(`tbl_derived2`.`tbl_derived2_id`), "
"BinToUuid(`T0`.`tbl_test2_id`), "
"`T0`.`u8_data`, "
"`T0`.`i8_data`, "
"`T0`.`u16_data`, "
"`T0`.`i16_data`, "
"BinToUuid(`T1`.`tbl_test2_id`), "
"`T1`.`u8_data`, "
"`T1`.`i8_data`, "
"`T1`.`u16_data`, "
"`T1`.`i16_data`, "
"BinToUuid(`T2`.`tbl_test2_id`), "
"`T2`.`u8_data`, "
"`T2`.`i8_data`, "
"`T2`.`u16_data`, "
"`T2`.`i16_data`, "
"BinToUuid(`tbl_derived3`.`tbl_derived3_id`) "
"FROM "
"`tbl_derived3` "
"JOIN "
"`tbl_derived2` ON `tbl_derived3`.`tbl_derived2_id`=`tbl_derived2`.`tbl_derived2_id` "
"JOIN "
"`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` "
"WHERE "
"(`tbl_derived3`.`tbl_derived3_id`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d1288ce-abb9-11e8-98d0-529269fb1459", "derived3", "3d1287a2-abb9-11e8-98d0-529269fb1459", nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, "3d12866c-abb9-11e8-98d0-529269fb1459" }
}));
expect_query(mock, "SELECT "
"BinToUuid(`tbl_test3`.`tbl_test3_id`), "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"FROM "
"`tbl_test3` "
"WHERE "
"(`tbl_test3`.`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d1289f0-abb9-11e8-98d0-529269fb1459", "100", "101", "102", "103" },
{ "3d128b26-abb9-11e8-98d0-529269fb1459", "110", "111", "112", "113" },
}));
expect_query(mock, "SELECT "
"BinToUuid(`tbl_test3`.`tbl_test3_id`), "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"FROM "
"`tbl_test3` "
"WHERE "
"(`tbl_test3`.`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d128eb4-abb9-11e8-98d0-529269fb1459", "120", "121", "122", "123" },
{ "3d128ffe-abb9-11e8-98d0-529269fb1459", "130", "131", "132", "133" },
{ "3d129134-abb9-11e8-98d0-529269fb1459", "140", "141", "142", "143" },
}));
expect_query(mock, "COMMIT");

EXPECT_CALL(
mock,
mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _))
.Times(AnyNumber())
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString()));

EXPECT_CALL(
mock,
mysql_close(
reinterpret_cast<MYSQL*>(0x1111)));

::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111));
auto context = make_context<driver::mariadb>(test_schema, connection);

derived3 d3;
d3.derived3_id = uuid("3d12866c-abb9-11e8-98d0-529269fb1459");

context.read(d3);

EXPECT_EQ (d3.id, uuid("3d1288ce-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d3.name, "derived3");
EXPECT_EQ (d3.derived2_id, uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"));
EXPECT_FALSE(static_cast<bool>(d3.test2_nullable));
EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_u));
EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_s));
EXPECT_EQ (d3.derived3_id, uuid("3d12866c-abb9-11e8-98d0-529269fb1459"));

{
auto it = d3.test3_list.begin();
ASSERT_NE(it, d3.test3_list.end());
EXPECT_EQ(it->id, uuid("3d1289f0-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 100);
EXPECT_EQ(it->i32_data, 101);
EXPECT_EQ(it->u64_data, 102);
EXPECT_EQ(it->i64_data, 103);
++it;

ASSERT_NE(it, d3.test3_list.end());
EXPECT_EQ(it->id, uuid("3d128b26-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 110);
EXPECT_EQ(it->i32_data, 111);
EXPECT_EQ(it->u64_data, 112);
EXPECT_EQ(it->i64_data, 113);
++it;

EXPECT_EQ(it, d3.test3_list.end());
}

{
auto it = d3.test3_vector.begin();
ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d128eb4-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 120);
EXPECT_EQ(it->i32_data, 121);
EXPECT_EQ(it->u64_data, 122);
EXPECT_EQ(it->i64_data, 123);
++it;

ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d128ffe-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 130);
EXPECT_EQ(it->i32_data, 131);
EXPECT_EQ(it->u64_data, 132);
EXPECT_EQ(it->i64_data, 133);
++it;

ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d129134-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 140);
EXPECT_EQ(it->i32_data, 141);
EXPECT_EQ(it->u64_data, 142);
EXPECT_EQ(it->i64_data, 143);
++it;

EXPECT_EQ(it, d3.test3_vector.end());
}
}

TEST(CppHibernateTests, read_base_ptr_dynamic)
{
StrictMock<mariadb_mock> mock;

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"`tbl_base`.`__type` AS `__type`, "
"BinToUuid(`tbl_base`.`tbl_base_id`), "
"`tbl_base`.`name`, "
"BinToUuid(`tbl_derived2`.`tbl_derived2_id`), "
"BinToUuid(`T0`.`tbl_test2_id`), "
"`T0`.`u8_data`, "
"`T0`.`i8_data`, "
"`T0`.`u16_data`, "
"`T0`.`i16_data`, "
"BinToUuid(`T1`.`tbl_test2_id`), "
"`T1`.`u8_data`, "
"`T1`.`i8_data`, "
"`T1`.`u16_data`, "
"`T1`.`i16_data`, "
"BinToUuid(`T2`.`tbl_test2_id`), "
"`T2`.`u8_data`, "
"`T2`.`i8_data`, "
"`T2`.`u16_data`, "
"`T2`.`i16_data`, "
"BinToUuid(`tbl_derived3`.`tbl_derived3_id`) "
"FROM "
"`tbl_derived2` "
"JOIN "
"`tbl_base` ON `tbl_derived2`.`tbl_base_id`=`tbl_base`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_test2` AS `T0` ON `tbl_derived2`.`tbl_test2_id_test2_nullable`=`T0`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T1` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_u`=`T1`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_test2` AS `T2` ON `tbl_derived2`.`tbl_test2_id_test2_ptr_s`=`T2`.`tbl_test2_id` "
"LEFT JOIN "
"`tbl_derived3` ON `tbl_derived2`.`tbl_derived2_id`=`tbl_derived3`.`tbl_derived2_id` "
"WHERE "
"(`tbl_derived2`.`tbl_derived2_id`=UuidToBin('X3d1287a2-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{
/* base */ "13", "3d1288ce-abb9-11e8-98d0-529269fb1459", "derived3",
/* derived2 */ "3d1287a2-abb9-11e8-98d0-529269fb1459",
/* derived2.test2_nullable */ nullptr, nullptr, nullptr, nullptr, nullptr,
/* derived2.test2_ptr_u */ nullptr, nullptr, nullptr, nullptr, nullptr,
/* derived2.test2_ptr_s */ nullptr, nullptr, nullptr, nullptr, nullptr,
/* derived3 */ "3d12866c-abb9-11e8-98d0-529269fb1459"
}
}));
expect_query(mock, "SELECT "
"BinToUuid(`tbl_test3`.`tbl_test3_id`), "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"FROM "
"`tbl_test3` "
"WHERE "
"(`tbl_test3`.`tbl_derived3_id_test3_list`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d1289f0-abb9-11e8-98d0-529269fb1459", "100", "101", "102", "103" },
{ "3d128b26-abb9-11e8-98d0-529269fb1459", "110", "111", "112", "113" },
}));
expect_query(mock, "SELECT "
"BinToUuid(`tbl_test3`.`tbl_test3_id`), "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"FROM "
"`tbl_test3` "
"WHERE "
"(`tbl_test3`.`tbl_derived3_id_test3_vector`=UuidToBin('X3d12866c-abb9-11e8-98d0-529269fb1459X')) ",
result_used({
{ "3d128eb4-abb9-11e8-98d0-529269fb1459", "120", "121", "122", "123" },
{ "3d128ffe-abb9-11e8-98d0-529269fb1459", "130", "131", "132", "133" },
{ "3d129134-abb9-11e8-98d0-529269fb1459", "140", "141", "142", "143" },
}));
expect_query(mock, "COMMIT");

EXPECT_CALL(
mock,
mysql_real_escape_string(reinterpret_cast<MYSQL*>(0x1111), _, _, _))
.Times(AnyNumber())
.WillRepeatedly(WithArgs<1, 2, 3>(EscapeString()));

EXPECT_CALL(
mock,
mysql_close(
reinterpret_cast<MYSQL*>(0x1111)));

::cppmariadb::connection connection(reinterpret_cast<MYSQL*>(0x1111));
auto context = make_context<driver::mariadb>(test_schema, connection);

constexpr decltype(auto) d2_key_field = test_schema.tables[5_c].fields[0_c];

std::unique_ptr<derived2> d2_ptr;
context.read(d2_ptr, where(equal(d2_key_field, "3d1287a2-abb9-11e8-98d0-529269fb1459")));
auto* d3_ptr = dynamic_cast<derived3*>(d2_ptr.get());
ASSERT_TRUE (d3_ptr);
auto& d3 = *d3_ptr;
EXPECT_EQ (d3.id, uuid("3d1288ce-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d3.name, "derived3");
EXPECT_EQ (d3.derived2_id, uuid("3d1287a2-abb9-11e8-98d0-529269fb1459"));
EXPECT_FALSE(static_cast<bool>(d3.test2_nullable));
EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_u));
EXPECT_FALSE(static_cast<bool>(d3.test2_ptr_s));
EXPECT_EQ (d3.derived3_id, uuid("3d12866c-abb9-11e8-98d0-529269fb1459"));

{
auto it = d3.test3_list.begin();
ASSERT_NE(it, d3.test3_list.end());
EXPECT_EQ(it->id, uuid("3d1289f0-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 100);
EXPECT_EQ(it->i32_data, 101);
EXPECT_EQ(it->u64_data, 102);
EXPECT_EQ(it->i64_data, 103);
++it;

ASSERT_NE(it, d3.test3_list.end());
EXPECT_EQ(it->id, uuid("3d128b26-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 110);
EXPECT_EQ(it->i32_data, 111);
EXPECT_EQ(it->u64_data, 112);
EXPECT_EQ(it->i64_data, 113);
++it;

EXPECT_EQ(it, d3.test3_list.end());
}

{
auto it = d3.test3_vector.begin();
ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d128eb4-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 120);
EXPECT_EQ(it->i32_data, 121);
EXPECT_EQ(it->u64_data, 122);
EXPECT_EQ(it->i64_data, 123);
++it;

ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d128ffe-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 130);
EXPECT_EQ(it->i32_data, 131);
EXPECT_EQ(it->u64_data, 132);
EXPECT_EQ(it->i64_data, 133);
++it;

ASSERT_NE(it, d3.test3_vector.end());
EXPECT_EQ(it->id, uuid("3d129134-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(it->u32_data, 140);
EXPECT_EQ(it->i32_data, 141);
EXPECT_EQ(it->u64_data, 142);
EXPECT_EQ(it->i64_data, 143);
++it;

EXPECT_EQ(it, d3.test3_vector.end());
}
}

Loading…
Cancel
Save