Browse Source

* implemented read method of mariadb driver for single foreign tables

master
bergmann 5 years ago
parent
commit
178a4711e9
8 changed files with 334 additions and 69 deletions
  1. +2
    -0
      include/cpphibernate/driver/mariadb/helper/context.h
  2. +11
    -5
      include/cpphibernate/driver/mariadb/schema/field.h
  3. +36
    -0
      include/cpphibernate/driver/mariadb/schema/field.inl
  4. +2
    -1
      src/driver/mariadb/schema/field.cpp
  5. +11
    -7
      src/driver/mariadb/schema/schema.cpp
  6. +98
    -22
      src/driver/mariadb/schema/table.cpp
  7. +11
    -7
      test/cpphibernate_init.cpp
  8. +163
    -27
      test/cpphibernate_read.cpp

+ 2
- 0
include/cpphibernate/driver/mariadb/helper/context.h View File

@@ -171,5 +171,7 @@ beg_namespace_cpphibernate_driver_mariadb
{ throw misc::hibernate_exception("read_context::finish_intern is not implemented!"); }
};

using read_context_ptr = std::unique_ptr<read_context>;

}
end_namespace_cpphibernate_driver_mariadb

+ 11
- 5
include/cpphibernate/driver/mariadb/schema/field.h View File

@@ -74,7 +74,10 @@ beg_namespace_cpphibernate_driver_mariadb
virtual void update ();

/* CRUD */
virtual value_t foreign_create_update (const create_update_context& context) const;
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;

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

virtual void update () override;
virtual value_t get (const data_context& context) const override;
virtual void set (const data_context& context, const value_t&) const override;
virtual void set (const data_context& context, const value_t& value) const override;
};

/* primary_key_field_t */
@@ -168,13 +171,16 @@ beg_namespace_cpphibernate_driver_mariadb
: public simple_field_t<T_field>
{
public:
using base_type = simple_field_t<T_field>;
using dataset_type = typename base_type::dataset_type;
using base_type = simple_field_t<T_field>;
using value_type = typename base_type::value_type;
using real_value_type = typename base_type::real_value_type;
using dataset_type = typename base_type::dataset_type;

using base_type::base_type;

public:
virtual value_t foreign_create_update(const create_update_context& context) const override;
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;
};

}

+ 36
- 0
include/cpphibernate/driver/mariadb/schema/field.inl View File

@@ -87,6 +87,42 @@ beg_namespace_cpphibernate_driver_mariadb
false);
}

template<typename T_field>
read_context_ptr foreign_table_field_t<T_field>
::foreign_read(const read_context& context, const value_t& value) 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);
});
}

namespace __impl
{



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

@@ -149,7 +149,8 @@ void field_t::update()

/* CRUD */

throw_not_implemented(value_t, foreign_create_update, const create_update_context&)
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&)

/* properties */



+ 11
- 7
src/driver/mariadb/schema/schema.cpp View File

@@ -203,13 +203,17 @@ void schema_t::init(const init_context& context) const
" 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"
" IF(\n"
" _bin IS NULL,\n"
" NULL,\n"
" LCASE(CONCAT_WS('-',\n"
" HEX(SUBSTR(_bin, 13, 4)),\n" // time low
" HEX(SUBSTR(_bin, 11, 2)),\n" // time mid
" HEX(SUBSTR(_bin, 9, 2)),\n" // time high and version
" HEX(SUBSTR(_bin, 7, 2)),\n" // clock sequence
" HEX(SUBSTR(_bin, 1, 6))\n" // node id
" )\n"
" )\n"
")";
exec_query();



+ 98
- 22
src/driver/mariadb/schema/table.cpp View File

@@ -10,6 +10,7 @@
#include <cpphibernate/driver/mariadb/schema/filter.h>

using namespace ::utl;
using namespace ::cpphibernate;
using namespace ::cpphibernate::driver::mariadb_impl;

/* data_extractor_t */
@@ -20,6 +21,7 @@ struct data_extractor_t
const read_context& _context;
const ::cppmariadb::row& _row;

const filter_t& _filter;
mutable size_t _index;

data_extractor_t(
@@ -29,6 +31,8 @@ struct data_extractor_t
: _table (p_table)
, _context (p_context)
, _row (p_row)
, _filter (p_context.filter)
, _index (0)
{ }

inline value_t get_value() const
@@ -40,23 +44,26 @@ struct data_extractor_t
return ret;
}

inline void read_field(const field_t& field) const
inline void read_field(const field_t& field, const read_context* context) const
{
field.set(_context, get_value());
if (context)
{
field.set(*context, get_value());
}
++_index;
}

inline void read_table(const table_t& table) const
inline void read_table(const table_t& table, const read_context* context) const
{
if (table.base_table)
read_table(*table.base_table);
read_table(*table.base_table, context);

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

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

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

/* foreign table one */
for (auto& ptr : table.foreign_table_one_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;

read_context_ptr next_context;
if (context)
next_context = field.foreign_read(*context, get_value());

read_table(ref_table, next_context.get());
}
}

@@ -72,7 +99,9 @@ struct data_extractor_t
{
_index = 0;
_context.emplace();
read_table(_table);
read_table(_table, &_context);
if (_index != _row.size())
throw misc::hibernate_exception("result was not completely read!");
}
};

@@ -84,7 +113,8 @@ struct select_query_builder_t
const filter_t& _filter;
bool _is_dynamic;

size_t index { 0 };
size_t alias_id { 0 };
size_t index { 0 };
std::ostringstream os;
std::ostringstream join;

@@ -97,36 +127,45 @@ struct select_query_builder_t
, _is_dynamic(p_is_dynamic)
{ }

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

inline void add_field(const field_t& field, const std::string& alias)
{
if (index++) os << ", ";
os << "`"
<< field.table_name
os << field.convert_from_open
<< "`"
<< alias
<< "`.`"
<< field.field_name
<< "`";
<< "`"
<< field.convert_from_close;
}

inline bool add_table(const table_t& table, const std::string& prefix)
inline bool add_table(const table_t& table, const std::string& alias)
{
bool ret = false;

if (table.base_table)
{
auto tmp = add_table(*table.base_table, "");
auto& base_table = *table.base_table;
auto base_alias = make_alias(base_table);
auto tmp = add_table(base_table, base_alias);
if (tmp)
{
assert(table.base_table->primary_key_field);
auto& base_key = *table.base_table->primary_key_field;
assert(base_table.primary_key_field);
auto& base_key = *base_table.primary_key_field;
ret = true;
join << " LEFT JOIN `"
<< table.table_name
<< base_table.table_name
<< "` AS `"
<< base_alias
<< "` ON `"
<< table.table_name
<< alias
<< "`.`"
<< base_key.field_name
<< "`=`"
<< base_key.table_name
<< base_alias
<< "`.`"
<< base_key.field_name
<< "`";
@@ -152,7 +191,7 @@ struct select_query_builder_t

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

/* data fields */
for (auto& ptr : table.data_fields)
@@ -160,7 +199,41 @@ struct select_query_builder_t
assert(ptr);
auto& field = *ptr;
if (!_filter.is_excluded(field))
add_field(field);
add_field(field, alias);
}

/* foreign table one */
for (auto& ptr : table.foreign_table_one_fields)
{
assert(ptr);
assert(ptr->referenced_table);
assert(ptr->referenced_table->primary_key_field);

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

if ( _filter.is_excluded(field)
|| _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
<< "`.`"
<< ref_key.table_name
<< "_id_"
<< field.field_name
<< "`=`"
<< new_alias
<< "`.`"
<< ref_key.field_name
<< "`";
}

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


+ 11
- 7
test/cpphibernate_init.cpp View File

@@ -39,13 +39,17 @@ TEST(CppHibernateTests, init)
" 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"
" IF(\n"
" _bin IS NULL,\n"
" NULL,\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"
" )\n"
")");

expect_query(mock, "CREATE TABLE IF NOT EXISTS `tbl_test1`\n"


+ 163
- 27
test/cpphibernate_read.cpp View File

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

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"`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` "
"BinToUuid(`T0`.`tbl_test1_id`), "
"`T0`.`str_data`, "
"`T0`.`str64_data`, "
"`T0`.`u32_nullable`, "
"`T0`.`u32_ptr_u`, "
"`T0`.`u32_ptr_s` "
"FROM "
"`tbl_test1` ",
"`tbl_test1` AS `T0` ",
result_used({
{ "3d12697a-abb9-11e8-98d0-529269fb1459", "str_data of class `test1` object `t1`", "str64_data of class `test1` object `t1`", nullptr, "123", "456" }
}));
@@ -48,6 +48,7 @@ TEST(CppHibernateTests, read_test1)
using namespace modifier;
context.read(t1);

EXPECT_EQ (t1.id, uuid("3d12697a-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (t1.str_data, "str_data of class `test1` object `t1`");
EXPECT_EQ (t1.str64_data, "str64_data of class `test1` object `t1`");
EXPECT_FALSE(static_cast<bool>(t1.u32_nullable));
@@ -63,13 +64,13 @@ TEST(CppHibernateTests, read_test2)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"`tbl_test2`.`tbl_test2_id`, "
"`tbl_test2`.`u8_data`, "
"`tbl_test2`.`i8_data`, "
"`tbl_test2`.`u16_data`, "
"`tbl_test2`.`i16_data` "
"BinToUuid(`T0`.`tbl_test2_id`), "
"`T0`.`u8_data`, "
"`T0`.`i8_data`, "
"`T0`.`u16_data`, "
"`T0`.`i16_data` "
"FROM "
"`tbl_test2` ",
"`tbl_test2` AS `T0` ",
result_used({
{ "3d1270dc-abb9-11e8-98d0-529269fb1459", "1", "2", "3", "4" }
}));
@@ -94,10 +95,11 @@ TEST(CppHibernateTests, read_test2)
test2 t2;
context.read(t2, where(equal(test2_key_field, "3d1270dc-abb9-11e8-98d0-529269fb1459")));

EXPECT_EQ(1, t2.u8_data);
EXPECT_EQ(2, t2.i8_data);
EXPECT_EQ(3, t2.u16_data);
EXPECT_EQ(4, t2.i16_data);
EXPECT_EQ(t2.id, uuid("3d1270dc-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(t2.u8_data, 1);
EXPECT_EQ(t2.i8_data, 2);
EXPECT_EQ(t2.u16_data, 3);
EXPECT_EQ(t2.i16_data, 4);
}

TEST(CppHibernateTests, read_test3)
@@ -106,13 +108,13 @@ TEST(CppHibernateTests, read_test3)

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"`tbl_test3`.`tbl_test3_id`, "
"`tbl_test3`.`u32_data`, "
"`tbl_test3`.`i32_data`, "
"`tbl_test3`.`u64_data`, "
"`tbl_test3`.`i64_data` "
"BinToUuid(`T0`.`tbl_test3_id`), "
"`T0`.`u32_data`, "
"`T0`.`i32_data`, "
"`T0`.`u64_data`, "
"`T0`.`i64_data` "
"FROM "
"`tbl_test3` ",
"`tbl_test3` AS `T0` ",
result_used({
{ "3d12737a-abb9-11e8-98d0-529269fb1459", "5", "6", "7", "8" }
}));
@@ -137,8 +139,142 @@ TEST(CppHibernateTests, read_test3)

context.read(t3);

EXPECT_EQ(5, t3.u32_data);
EXPECT_EQ(6, t3.i32_data);
EXPECT_EQ(7, t3.u64_data);
EXPECT_EQ(8, t3.i64_data);
EXPECT_EQ(t3.id, uuid("3d12737a-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ(t3.u32_data, 5);
EXPECT_EQ(t3.i32_data, 6);
EXPECT_EQ(t3.u64_data, 7);
EXPECT_EQ(t3.i64_data, 8);
}

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

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` "
"FROM "
"`tbl_derived1` AS `T0` "
"LEFT JOIN "
"`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_test1` AS `T2` ON `T0`.`tbl_test1_id_test1_data`=`T2`.`tbl_test1_id` ",
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" }
}));
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);

derived1 d1;
d1.derived1_id = uuid("3d12758c-abb9-11e8-98d0-529269fb1459");

context.read(d1);

EXPECT_EQ (d1.id, uuid("3d12778a-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d1.derived1_id, uuid("3d12758c-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d1.name, "derived1");
EXPECT_EQ (d1.enum_data, test_enum::test2);
EXPECT_EQ (d1.test1_data.str_data, "str_data of class `test1` object `d1.test1_data`");
EXPECT_EQ (d1.test1_data.str64_data, "str64_data of class `test1` object `d1.test1_data`");
ASSERT_TRUE (static_cast<bool>(d1.test1_data.u32_nullable));
EXPECT_EQ (*d1.test1_data.u32_nullable, 32);
ASSERT_TRUE (static_cast<bool>(d1.test1_data.u32_ptr_s));
EXPECT_EQ (*d1.test1_data.u32_ptr_s, 789);
EXPECT_FALSE(static_cast<bool>(d1.test1_data.u32_ptr_u));
}

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

expect_query(mock, "START TRANSACTION");
expect_query(mock, "SELECT "
"BinToUuid(`T1`.`tbl_base_id`), "
"`T1`.`name`, "
"BinToUuid(`T0`.`tbl_derived2_id`), "
"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` "
"FROM "
"`tbl_derived2` AS `T0` "
"LEFT JOIN "
"`tbl_base` AS `T1` ON `T0`.`tbl_base_id`=`T1`.`tbl_base_id` "
"LEFT JOIN "
"`tbl_test2` AS `T2` ON `T0`.`tbl_test2_id_test2_nullable`=`T2`.`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` ",
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 }
}));
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);

derived2 d2;
d2.derived2_id = uuid("3d127bcc-abb9-11e8-98d0-529269fb1459");

context.read(d2);

EXPECT_EQ (d2.id, uuid("3d127db6-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d2.derived2_id, uuid("3d127bcc-abb9-11e8-98d0-529269fb1459"));
EXPECT_EQ (d2.name, "derived2");
ASSERT_TRUE (static_cast<bool>(d2.test2_nullable));
EXPECT_EQ (d2.test2_nullable->u8_data, 10);
EXPECT_EQ (d2.test2_nullable->i8_data, 11);
EXPECT_EQ (d2.test2_nullable->u16_data, 12);
EXPECT_EQ (d2.test2_nullable->i16_data, 13);
ASSERT_TRUE (static_cast<bool>(d2.test2_ptr_u));
EXPECT_EQ (d2.test2_ptr_u->u8_data, 20);
EXPECT_EQ (d2.test2_ptr_u->i8_data, 21);
EXPECT_EQ (d2.test2_ptr_u->u16_data, 22);
EXPECT_EQ (d2.test2_ptr_u->i16_data, 23);
EXPECT_FALSE(static_cast<bool>(d2.test2_ptr_s));
}

Loading…
Cancel
Save