| @@ -10,7 +10,136 @@ Before you can use cpphibernate you need to download and install [the official m | |||
| [The cpputils libary](https://git.bergmann89.de/cpp/cpputils) and [the cppmariadb library](https://github.com/Bergmann89/cppmariadb) will be automatically downladed during the build. | |||
| ### Small Usage Example | |||
| ### Simple Usage Example | |||
| In this simple example we use a single and simple object, that is stored in the database. The create, update, read and destroy methods and the corresponding SQL queries are shown in the code below. | |||
| ```cpp | |||
| #include <memory> | |||
| #include <iostream> | |||
| #include <exception> | |||
| #include <cpphibernate.h> | |||
| #include <cpphibernate/driver/mariadb.h> | |||
| /* define the class to hibernate */ | |||
| struct test_data | |||
| { | |||
| size_t id; //!< ID of the class instance (this field is mandatory for the hibernate engine) | |||
| std::string str_data; //!< Add string data field | |||
| ::cpphibernate::string<64> str64_data; //!< Add a special string data field with a mex length of 64 | |||
| uint32_t u32; //!< Add a normal integer data field | |||
| std::unique_ptr<uint32_t> u32_ptr_u; //!< Add a nullable integer data field | |||
| }; | |||
| /* create the database schema */ | |||
| constexpr decltype(auto) test_schema = cpphibernate_make_schema( | |||
| test_schema, // this is the schema name | |||
| cpphibernate_make_table_name( | |||
| tbl_test_data, // this is the table name | |||
| test_data, // this is the referenced class | |||
| 1, // a unique id for the table | |||
| cpphibernate_make_id (&test_data::id), // pointer to the ID member | |||
| cpphibernate_make_field (test_data, str_data), // define a normal member field | |||
| cpphibernate_make_field (test_data, str64_data), // [...] | |||
| cpphibernate_make_field (test_data, u32), // [...] | |||
| cpphibernate_make_field (test_data, u32_ptr_u), // [...] | |||
| ) | |||
| ); | |||
| int main(int argc, char** argv) | |||
| { | |||
| try | |||
| { | |||
| using namespace ::cppmariadb; | |||
| using namespace ::cpphibernate; | |||
| /* establish connection to database */ | |||
| connection c = database::connect("localhost", 3306, "testuser", "password", "", client_flags::empty()); | |||
| /* create a hibernation context */ | |||
| auto context = make_context_ptr<driver::mariadb>(test_schema, c); | |||
| /* initialize the database schema */ | |||
| context.init(); /* CREATE SCHEMA IF NOT EXISTS `test_schema` DEFAULT CHARACTER SET utf8; | |||
| USE `test_schema`; | |||
| CREATE TABLE IF NOT EXISTS `tbl_test_data` | |||
| ( | |||
| `tbl_test_data_id` INT UNSIGNED NOT NULL, | |||
| `str_data` VARCHAR(100) NOT NULL, | |||
| `str64_data` VARCHAR(64) NOT NULL, | |||
| `u32` INT UNSIGNED NOT NULL, | |||
| `u32_ptr_u` INT UNSIGNED NULL DEFAULT NULL, | |||
| PRIMARY KEY ( `tbl_test_data_id` ), | |||
| UNIQUE INDEX `index_tbl_test_data_id` ( `tbl_test_data_id` ASC ) | |||
| ) | |||
| ENGINE = InnoDB | |||
| DEFAULT CHARACTER SET = utf8; */ | |||
| /* create some test data */ | |||
| test_data data; | |||
| data.str_data = "this is a simple string"; | |||
| data.str64_data = "this is a string with max 64 characters"; | |||
| data.u32 = 123; | |||
| /* create a new dataset in the database: | |||
| * the new IDs of the object are stored in the corresponding members */ | |||
| context.create(data); /* INSERT INTO | |||
| `tbl_test_data` | |||
| SET | |||
| `str_data`='this is a simple string', | |||
| `str64_data`='this is a string with max 64 characters', | |||
| `u32`=123, | |||
| `u32_ptr_u`=NULL; */ | |||
| /* change some data and update the database */ | |||
| t1.u32_ptr_u.reset(new uint32_t(456)); | |||
| context.update(data); /* UPDATE | |||
| `tbl_test_data` | |||
| SET | |||
| `str_data`='this is a simple string', | |||
| `str64_data`='this is a string with max 64 characters', | |||
| `u32`=123, | |||
| `u32_ptr_u`=456 | |||
| WHERE | |||
| `tbl_test_data_id`=1; */ | |||
| /* read back the created object: | |||
| * if no selector is passed here, the ID member of the object is used */ | |||
| context.read(data); /* SELECT " | |||
| `tbl_test_data`.`tbl_test_data_id`), | |||
| `tbl_test_data`.`str_data`, | |||
| `tbl_test_data`.`str64_data`, | |||
| `tbl_test_data`.`u32`, | |||
| `tbl_test_data`.`u32_ptr_u` | |||
| FROM | |||
| `tbl_test_data` | |||
| WHERE | |||
| `tbl_test_data`.`tbl_test_data_id`=1; */ | |||
| /* delete the object from the database */ | |||
| context.destroy(data); /* DELETE | |||
| `tbl_test_data` | |||
| FROM | |||
| `tbl_test_data` | |||
| WHERE | |||
| `tbl_test_data`.`tbl_test_data_id`=1; */ | |||
| return 0; | |||
| } | |||
| catch(const std::exception& ex) | |||
| { | |||
| std::cout << ex.what() << std::endl; | |||
| } | |||
| return 1 | |||
| } | |||
| ``` | |||
| ### More Advanced Usage Example | |||
| The more advanced example uses a more complex database schema, containing custom defined enum types, one to one and one to many correleation of different tables, a polymorphic class hierarchy with inheritance and some of the std containers like std::unique_ptr or std::vector. | |||
| ```cpp | |||
| #include <memory> | |||
| @@ -14,25 +14,25 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| { | |||
| ::cppmariadb::statement statement; | |||
| limit_builder(const T_modifiers& modifier) | |||
| limit_builder(const T_modifiers& p_modifier) | |||
| { | |||
| ssize_t limit = -1; | |||
| ssize_t offset = -1; | |||
| hana::for_each(modifier, [&limit, &offset](auto& modifier){ | |||
| using modifier_type = mp::decay_t<decltype(modifier)>; | |||
| hana::for_each(p_modifier, [&limit, &offset](auto& x_modifier){ | |||
| using modifier_type = mp::decay_t<decltype(x_modifier)>; | |||
| using is_limit_type = modifier::is_limit_modifier<modifier_type>; | |||
| using is_offset_type = modifier::is_offset<modifier_type>; | |||
| hana::eval_if( | |||
| is_limit_type { }, | |||
| [&limit, &modifier](auto _){ | |||
| limit = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||
| [&limit, &x_modifier](auto _){ | |||
| limit = static_cast<ssize_t>(hana::value(_(x_modifier).value)); | |||
| }, | |||
| [&offset, &modifier](){ | |||
| [&offset, &x_modifier](){ | |||
| hana::eval_if( | |||
| is_offset_type { }, | |||
| [&offset, &modifier](auto _){ | |||
| offset = static_cast<ssize_t>(hana::value(_(modifier).value)); | |||
| [&offset, &x_modifier](auto _){ | |||
| offset = static_cast<ssize_t>(hana::value(_(x_modifier).value)); | |||
| }, | |||
| []{ | |||
| /* no-op */ | |||
| @@ -26,51 +26,51 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| { } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| inline auto build_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_and<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "("; | |||
| build_clause(os, clause.clauses[hana::size_c<0>]); | |||
| build_clause(os, p_clause.clauses[hana::size_c<0>]); | |||
| os << ")"; | |||
| hana::for_each( | |||
| hana::remove_at(clause.clauses, hana::size_c<0>), | |||
| [&](auto& clause) { | |||
| hana::remove_at(p_clause.clauses, hana::size_c<0>), | |||
| [&](auto& x_clause) { | |||
| os << " AND ("; | |||
| build_clause(os, clause); | |||
| build_clause(os, x_clause); | |||
| os << ")"; | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| inline auto build_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_or<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "("; | |||
| build_clause(os, clause.clauses[hana::size_c<0>]); | |||
| build_clause(os, p_clause.clauses[hana::size_c<0>]); | |||
| os << ")"; | |||
| hana::for_each( | |||
| hana::remove_at(clause.clauses, hana::size_c<0>), | |||
| [&](auto& clause) { | |||
| hana::remove_at(p_clause.clauses, hana::size_c<0>), | |||
| [&](auto& x_clause) { | |||
| os << " OR ("; | |||
| build_clause(os, clause); | |||
| build_clause(os, x_clause); | |||
| os << ")"; | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| inline auto build_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
| { | |||
| os << "NOT ("; | |||
| build_clause(os, clause.clause); | |||
| build_clause(os, p_clause.clause); | |||
| os << ")"; | |||
| } | |||
| template<typename T_clause> | |||
| inline auto build_clause(T_clause&& clause) | |||
| inline auto build_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
| { | |||
| auto field_id = misc::get_type_id(hana::type_c<mp::decay_t<decltype(clause.field)>>); | |||
| auto field_id = misc::get_type_id(hana::type_c<mp::decay_t<decltype(p_clause.field)>>); | |||
| auto& field = schema.field(field_id); | |||
| os << "`" | |||
| << field.table_name | |||
| @@ -114,28 +114,28 @@ beg_namespace_cpphibernate_driver_mariadb | |||
| { } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| inline auto assign_clause(T_clause&& p_clause) | |||
| -> mp::enable_if_c< | |||
| modifier::is_where_clause_and<mp::decay_t<T_clause>>::value | |||
| || modifier::is_where_clause_or <mp::decay_t<T_clause>>::value> | |||
| { | |||
| hana::for_each([&](auto& clause) { | |||
| assign_clause(clause); | |||
| hana::for_each(std::forward<T_clause>(p_clause).clauses, [&](auto& x_clause) { | |||
| assign_clause(x_clause); | |||
| }); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| inline auto assign_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_not<mp::decay_t<T_clause>>> | |||
| { | |||
| assign_clause(clause.clause); | |||
| assign_clause(std::forward<T_clause>(p_clause).clause); | |||
| } | |||
| template<typename T_clause> | |||
| inline auto assign_clause(T_clause&& clause) | |||
| inline auto assign_clause(T_clause&& p_clause) | |||
| -> mp::enable_if<modifier::is_where_clause_equal<mp::decay_t<T_clause>>> | |||
| { | |||
| statement.set(index, clause.value); | |||
| statement.set(index, std::forward<T_clause>(p_clause).value); | |||
| ++index; | |||
| } | |||
| @@ -40,4 +40,4 @@ | |||
| cpphibernate_make_field_name( \ | |||
| id, \ | |||
| p_member_ptr, \ | |||
| cpphibernate::schema::attribute::primary_key) | |||
| cpphibernate::schema::attribute::primary_key) | |||
| @@ -111,8 +111,8 @@ void schema_t::print(std::ostream& os) const | |||
| os << indent << '{' | |||
| << incindent | |||
| << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
| << indent << "\"tables\": " << misc::print_container(tables, true, [](auto& os, auto& kvp) { | |||
| kvp.second->print(os); | |||
| << indent << "\"tables\": " << misc::print_container(tables, true, [](auto& s, auto& kvp) { | |||
| kvp.second->print(s); | |||
| }) | |||
| << decindent | |||
| << indent << '}'; | |||
| @@ -1316,28 +1316,28 @@ void table_t::print(std::ostream& os) const | |||
| << indent << "\"derived_dataset_ids\": " << misc::print_container(derived_dataset_ids, false) << "," | |||
| << indent << "\"schema_name\": \"" << schema_name << "\"," | |||
| << indent << "\"table_name\": \"" << table_name << "\"," | |||
| << indent << "\"fields\":" << misc::print_container(fields, true, [](auto& os, auto& field) { | |||
| field->print(os); | |||
| << indent << "\"fields\":" << misc::print_container(fields, true, [](auto& s, auto& field) { | |||
| field->print(s); | |||
| }) << "," | |||
| << indent << "\"base_table\": " << (base_table ? std::string("\"") + base_table->table_name + "\"" : "null") << "," | |||
| << indent << "\"derived_tables\":" << misc::print_container(derived_tables, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->table_name << '"'; | |||
| << indent << "\"derived_tables\":" << misc::print_container(derived_tables, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->table_name << '"'; | |||
| }) << "," | |||
| << indent << "\"primary_key_field\": " << (primary_key_field ? std::string("\"") + primary_key_field->field_name + "\"" : "null") << "," | |||
| << indent << "\"foreign_key_fields\": " << misc::print_container(foreign_key_fields, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->table_name << '.' << ptr->field_name << '"'; | |||
| << indent << "\"foreign_key_fields\": " << misc::print_container(foreign_key_fields, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->table_name << '.' << ptr->field_name << '"'; | |||
| }) << "," | |||
| << indent << "\"foreign_table_fields\": " << misc::print_container(foreign_table_fields, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->field_name << '"'; | |||
| << indent << "\"foreign_table_fields\": " << misc::print_container(foreign_table_fields, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->field_name << '"'; | |||
| }) << "," | |||
| << indent << "\"foreign_table_one_fields\": " << misc::print_container(foreign_table_one_fields, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->field_name << '"'; | |||
| << indent << "\"foreign_table_one_fields\": " << misc::print_container(foreign_table_one_fields, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->field_name << '"'; | |||
| }) << "," | |||
| << indent << "\"foreign_table_many_fields\": " << misc::print_container(foreign_table_many_fields, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->field_name << '"'; | |||
| << indent << "\"foreign_table_many_fields\": " << misc::print_container(foreign_table_many_fields, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->field_name << '"'; | |||
| }) << "," | |||
| << indent << "\"data_fields\": " << misc::print_container(data_fields, true, [](auto& os, auto& ptr){ | |||
| os << indent << '"' << ptr->field_name << '"'; | |||
| << indent << "\"data_fields\": " << misc::print_container(data_fields, true, [](auto& s, auto& ptr){ | |||
| s << indent << '"' << ptr->field_name << '"'; | |||
| }) | |||
| << decindent | |||
| << indent << '}'; | |||
| @@ -37,11 +37,11 @@ bool uuid::from_string(const std::string& str, uuid& val) | |||
| while(i < 32 && c < e) | |||
| { | |||
| if (*c >= '0' && *c <= '9') | |||
| val[i >> 1] |= ((*c - '0') << (4 * (1 - (i & 1)))); | |||
| val[i >> 1] = static_cast<uint8_t>(val[i >> 1] | ((*c - '0' + 0) << (4 * (1 - (i & 1))))); | |||
| else if (*c >= 'a' && *c <= 'f') | |||
| val[i >> 1] |= ((*c - 'a' + 10) << (4 * (1 - (i & 1)))); | |||
| val[i >> 1] = static_cast<uint8_t>(val[i >> 1] | ((*c - 'a' + 10) << (4 * (1 - (i & 1))))); | |||
| else if (*c >= 'A' && *c <= 'F') | |||
| val[i >> 1] |= ((*c - 'A' + 10) << (4 * (1 - (i & 1)))); | |||
| val[i >> 1] = static_cast<uint8_t>(val[i >> 1] | ((*c - 'A' + 10) << (4 * (1 - (i & 1))))); | |||
| else if (*c != '-') | |||
| return false; | |||
| if (*c != '-') | |||
| @@ -18,9 +18,9 @@ List ( FILTER SOURCE_FILES EXCLUDE REGEX "/_[A-Za-z0-9_-] | |||
| Add_Executable ( test_cpphibernate EXCLUDE_FROM_ALL ${SOURCE_FILES} ) | |||
| Target_Link_Libraries ( test_cpphibernate | |||
| cpphibernate | |||
| gtest | |||
| gmock | |||
| gmock_main | |||
| gmock | |||
| gtest | |||
| pthread ) | |||
| If ( __COTIRE_INCLUDED ) | |||
| Cotire ( test_cpphibernate ) | |||