diff --git a/README.md b/README.md index 4ba0e72..10c557f 100644 --- a/README.md +++ b/README.md @@ -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 +#include +#include +#include +#include + +/* 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 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(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 diff --git a/include/cpphibernate/driver/mariadb/impl/limit.h b/include/cpphibernate/driver/mariadb/impl/limit.h index 594f973..9076137 100644 --- a/include/cpphibernate/driver/mariadb/impl/limit.h +++ b/include/cpphibernate/driver/mariadb/impl/limit.h @@ -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; + hana::for_each(p_modifier, [&limit, &offset](auto& x_modifier){ + using modifier_type = mp::decay_t; using is_limit_type = modifier::is_limit_modifier; using is_offset_type = modifier::is_offset; hana::eval_if( is_limit_type { }, - [&limit, &modifier](auto _){ - limit = static_cast(hana::value(_(modifier).value)); + [&limit, &x_modifier](auto _){ + limit = static_cast(hana::value(_(x_modifier).value)); }, - [&offset, &modifier](){ + [&offset, &x_modifier](){ hana::eval_if( is_offset_type { }, - [&offset, &modifier](auto _){ - offset = static_cast(hana::value(_(modifier).value)); + [&offset, &x_modifier](auto _){ + offset = static_cast(hana::value(_(x_modifier).value)); }, []{ /* no-op */ diff --git a/include/cpphibernate/driver/mariadb/impl/where.h b/include/cpphibernate/driver/mariadb/impl/where.h index 1c81709..6db3582 100644 --- a/include/cpphibernate/driver/mariadb/impl/where.h +++ b/include/cpphibernate/driver/mariadb/impl/where.h @@ -26,51 +26,51 @@ beg_namespace_cpphibernate_driver_mariadb { } template - inline auto build_clause(T_clause&& clause) + inline auto build_clause(T_clause&& p_clause) -> mp::enable_if>> { 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 - inline auto build_clause(T_clause&& clause) + inline auto build_clause(T_clause&& p_clause) -> mp::enable_if>> { 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 - inline auto build_clause(T_clause&& clause) + inline auto build_clause(T_clause&& p_clause) -> mp::enable_if>> { os << "NOT ("; - build_clause(os, clause.clause); + build_clause(os, p_clause.clause); os << ")"; } template - inline auto build_clause(T_clause&& clause) + inline auto build_clause(T_clause&& p_clause) -> mp::enable_if>> { - auto field_id = misc::get_type_id(hana::type_c>); + auto field_id = misc::get_type_id(hana::type_c>); auto& field = schema.field(field_id); os << "`" << field.table_name @@ -114,28 +114,28 @@ beg_namespace_cpphibernate_driver_mariadb { } template - inline auto assign_clause(T_clause&& clause) + inline auto assign_clause(T_clause&& p_clause) -> mp::enable_if_c< modifier::is_where_clause_and>::value || modifier::is_where_clause_or >::value> { - hana::for_each([&](auto& clause) { - assign_clause(clause); + hana::for_each(std::forward(p_clause).clauses, [&](auto& x_clause) { + assign_clause(x_clause); }); } template - inline auto assign_clause(T_clause&& clause) + inline auto assign_clause(T_clause&& p_clause) -> mp::enable_if>> { - assign_clause(clause.clause); + assign_clause(std::forward(p_clause).clause); } template - inline auto assign_clause(T_clause&& clause) + inline auto assign_clause(T_clause&& p_clause) -> mp::enable_if>> { - statement.set(index, clause.value); + statement.set(index, std::forward(p_clause).value); ++index; } diff --git a/include/cpphibernate/schema/macros.h b/include/cpphibernate/schema/macros.h index bd778f9..bd213d6 100644 --- a/include/cpphibernate/schema/macros.h +++ b/include/cpphibernate/schema/macros.h @@ -40,4 +40,4 @@ cpphibernate_make_field_name( \ id, \ p_member_ptr, \ - cpphibernate::schema::attribute::primary_key) \ No newline at end of file + cpphibernate::schema::attribute::primary_key) diff --git a/src/cpphibernate/driver/mariadb/schema/schema.cpp b/src/cpphibernate/driver/mariadb/schema/schema.cpp index d4b8fad..68fb45a 100644 --- a/src/cpphibernate/driver/mariadb/schema/schema.cpp +++ b/src/cpphibernate/driver/mariadb/schema/schema.cpp @@ -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 << '}'; diff --git a/src/cpphibernate/driver/mariadb/schema/table.cpp b/src/cpphibernate/driver/mariadb/schema/table.cpp index 5e7f544..6e2d9f3 100644 --- a/src/cpphibernate/driver/mariadb/schema/table.cpp +++ b/src/cpphibernate/driver/mariadb/schema/table.cpp @@ -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 << '}'; diff --git a/src/cpphibernate/types.cpp b/src/cpphibernate/types.cpp index 7c39a8c..a2a9256 100644 --- a/src/cpphibernate/types.cpp +++ b/src/cpphibernate/types.cpp @@ -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(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(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(val[i >> 1] | ((*c - 'A' + 10) << (4 * (1 - (i & 1))))); else if (*c != '-') return false; if (*c != '-') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 73e13ac..de3903d 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -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 )