diff --git a/example/main.cpp b/example/main.cpp index 22e1a3a6..994ca02b 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -18,12 +18,13 @@ using namespace ormpp; const char *password = ""; const char *ip = "127.0.0.1"; +const char *username = "root"; const char *db = "test_ormppdb"; struct person { - int id; std::optional name; std::optional age; + int id; }; REFLECTION(person, id, name, age) @@ -38,11 +39,11 @@ int main() { #ifdef ORMPP_ENABLE_MYSQL { dbng mysql; - if (mysql.connect(ip, "root", password, db)) { + if (mysql.connect(ip, username, password, db)) { mysql.create_datatable(ormpp_auto_key{"id"}); mysql.delete_records(); - mysql.insert({0, "purecpp"}); - mysql.insert({0, "purecpp", 6}); + mysql.insert({"purecpp"}); + mysql.insert({"purecpp", 6}); } else { std::cout << "connect fail" << std::endl; @@ -50,7 +51,7 @@ int main() { } { - connection_pool>::instance().init(4, ip, "root", password, db, + connection_pool>::instance().init(4, ip, username, password, db, 5, 3306); auto conn = connection_pool>::instance().get(); conn_guard guard(conn); @@ -68,10 +69,10 @@ int main() { { sqlite.delete_records(); - sqlite.insert({0, "purecpp"}); - sqlite.insert({0, "purecpp", 6}); + sqlite.insert({"purecpp"}); + sqlite.insert({"purecpp", 6}); auto vec = sqlite.query(); - for (auto &[id, name, age] : vec) { + for (auto &[name, age, id] : vec) { std::cout << id << ", " << *name << ", " << *age << "\n"; } } @@ -84,13 +85,13 @@ int main() { sqlite.insert({0, "purecpp", 3}); { auto vec = sqlite.query("name='purecpp'", "order by age desc"); - for (auto &[id, name, age] : vec) { + for (auto &[name, age, id] : vec) { std::cout << id << ", " << name << ", " << age << "\n"; } } { auto vec = sqlite.query("age=3", "order by id desc", "limit 1"); - for (auto &[id, name, age] : vec) { + for (auto &[name, age, id] : vec) { std::cout << id << ", " << name << ", " << age << "\n"; } } diff --git a/include/mysql.hpp b/include/mysql.hpp index 9c636d24..62968b78 100644 --- a/include/mysql.hpp +++ b/include/mysql.hpp @@ -449,6 +449,9 @@ class mysql { // just support execute string sql without placeholders bool execute(const std::string &sql) { +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif reset_error(); if (mysql_query(con_, sql.data()) != 0) { set_last_error(mysql_error(con_)); diff --git a/include/postgresql.hpp b/include/postgresql.hpp index 64690e8e..c396ce82 100644 --- a/include/postgresql.hpp +++ b/include/postgresql.hpp @@ -75,93 +75,22 @@ class postgresql { template constexpr int insert(const T &t, Args &&...args) { - std::string sql = generate_auto_insert_sql(auto_key_map_, false); - if (!prepare(sql)) - return INT_MIN; - - return insert_impl(sql, t, std::forward(args)...); + return insert_or_update(false, t, std::forward(args)...); } template constexpr int insert(const std::vector &v, Args &&...args) { - std::string sql = generate_auto_insert_sql(auto_key_map_, false); - - if (!begin()) - return INT_MIN; - - if (!prepare(sql)) - return INT_MIN; - - for (auto &item : v) { - auto result = insert_impl(sql, item, std::forward(args)...); - if (result == INT_MIN) { - rollback(); - return INT_MIN; - } - } - - if (!commit()) - return INT_MIN; - - return (int)v.size(); + return insert_or_update(false, v, std::forward(args)...); } - // if there is no key in a table, you can set some fields as a condition in - // the args... template constexpr int update(const T &t, Args &&...args) { - // transaction, firstly delete, secondly insert - auto name = get_name(); - auto it = key_map_.find(name.data()); - auto key = it == key_map_.end() ? "" : it->second; - - auto condition = get_condition(t, key, std::forward(args)...); - if (!begin()) - return INT_MIN; - - if (!delete_records(condition)) { - rollback(); - return INT_MIN; - } - - if (insert(t) < 0) { - rollback(); - return INT_MIN; - } - - if (!commit()) - return INT_MIN; - - return 1; + return insert_or_update(true, t, std::forward(args)...); } template constexpr int update(const std::vector &v, Args &&...args) { - // transaction, firstly delete, secondly insert - if (!begin()) - return INT_MIN; - - auto name = get_name(); - auto it = key_map_.find(name.data()); - auto key = it == key_map_.end() ? "" : it->second; - for (auto &t : v) { - auto condition = get_condition(t, key, std::forward(args)...); - - if (!delete_records(condition)) { - rollback(); - return INT_MIN; - } - - if (insert(t) < 0) { - rollback(); - return INT_MIN; - } - } - - if (!commit()) - return INT_MIN; - - return (int)v.size(); + return insert_or_update(true, v, std::forward(args)...); } template @@ -253,16 +182,14 @@ class postgresql { template constexpr bool delete_records(Args &&...where_conditon) { auto sql = generate_delete_sql(std::forward(where_conditon)...); -#ifdef ORMPP_ENABLE_LOG - std::cout << sql << std::endl; -#endif - res_ = PQexec(con_, sql.data()); - auto guard = guard_statment(res_); - return PQresultStatus(res_) == PGRES_COMMAND_OK; + return execute(sql); } // just support execute string sql without placeholders auto execute(const std::string &sql) { +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif res_ = PQexec(con_, sql.data()); auto guard = guard_statment(res_); return PQresultStatus(res_) == PGRES_COMMAND_OK; @@ -435,14 +362,11 @@ class postgresql { } template - constexpr int insert_impl(const std::string &sql, const T &t, - Args &&...args) { -#ifdef ORMPP_ENABLE_LOG - std::cout << sql << std::endl; -#endif + constexpr int insert_impl(bool update, const T &t, Args &&...args) { std::vector> param_values; auto it = auto_key_map_.find(get_name()); - std::string auto_key = (it == auto_key_map_.end()) ? "" : it->second; + std::string auto_key = + (update || it == auto_key_map_.end()) ? "" : it->second; iguana::for_each(t, [&t, ¶m_values, auto_key, this](auto item, auto i) { if (!auto_key.empty() && auto_key == iguana::get_name(decltype(i)::value).data()) { @@ -466,6 +390,89 @@ class postgresql { return PQresultStatus(res_) == PGRES_COMMAND_OK ? 1 : INT_MIN; } + template + constexpr int insert_or_update(bool update, const T &t, Args &&...args) { + std::string sql = + update ? generate_update_sql(std::forward(args)...) + : generate_auto_insert_sql(auto_key_map_, false); +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif + if (!prepare(sql)) + return INT_MIN; + + return insert_impl(update, t, std::forward(args)...); + } + + template + constexpr int insert_or_update(bool update, const std::vector &v, + Args &&...args) { + std::string sql = + update ? generate_update_sql(std::forward(args)...) + : generate_auto_insert_sql(auto_key_map_, false); +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif + if (!begin()) + return INT_MIN; + + if (!prepare(sql)) + return INT_MIN; + + for (auto &item : v) { + auto result = insert_impl(update, item, std::forward(args)...); + if (result == INT_MIN) { + rollback(); + return INT_MIN; + } + } + + if (!commit()) + return INT_MIN; + + return (int)v.size(); + } + + template + inline std::string generate_update_sql(Args &&...args) { + constexpr auto SIZE = iguana::get_value(); + std::string sql = "insert into "; + auto name = get_name(); + append(sql, name.data()); + int index = 0; + std::string set; + std::string fields = "("; + std::string values = "values("; + auto it = key_map_.find(name.data()); + for (auto i = 0; i < SIZE; ++i) { + std::string field_name = iguana::get_name(i).data(); + std::string value = "$" + std::to_string(++index); + set += field_name + "=" + value; + fields += field_name; + values += value; + if (i < SIZE - 1) { + fields += ", "; + values += ", "; + set += ", "; + } + else { + fields += ")"; + values += ")"; + set += ";"; + } + } + std::string conflict = "on conflict("; + if constexpr (sizeof...(args) > 0) { + append(conflict, args...); + } + else { + conflict += it->second; + } + conflict += ")"; + append(sql, fields, values, conflict, "do update set", set); + return sql; + } + template constexpr void set_param_values(std::vector> ¶m_values, T &&value) { @@ -543,58 +550,6 @@ class postgresql { } } - template - constexpr std::string get_condition(const T &t, const std::string &key, - Args &&...args) { - std::string result = ""; - constexpr auto SIZE = iguana::get_value(); - iguana::for_each(t, [&](auto &item, auto i) { - constexpr auto Idx = decltype(i)::value; - using U = std::remove_reference_t( - std::declval()))>; - if (!key.empty() && key == iguana::get_name().data()) { - if constexpr (std::is_arithmetic_v) { - append(result, iguana::get_name().data(), "=", - std::to_string(t.*item)); - } - else if constexpr (std::is_same_v) { - append(result, iguana::get_name().data(), "=", t.*item); - } - } - - // if constexpr (sizeof...(Args)>0){ - build_condition_by_key(result, t.*item, args...); - //(test(args), ...); - // (build_condition_by_key_impl(result, t.*item, args),...); - ////can't pass U in vs2017 - //} - }); - - return result; - } - - template - void build_condition_by_key(std::string &result, const V &t, Args... args) { - (build_condition_by_key(result, t, args), ...); - } - - template - void build_condition_by_key_impl(std::string &result, const V &val, W &key) { - using U = - std::remove_reference_t(std::declval()))>; - if (key == iguana::get_name().data()) { - if constexpr (std::is_arithmetic_v) { - append(result, " and "); - append(result, iguana::get_name().data(), "=", - std::to_string(val)); - } - else if constexpr (std::is_same_v) { - append(result, " and "); - append(result, iguana::get_name().data(), "=", val); - } - } - } - private: struct guard_statment { guard_statment(PGresult *res) : res_(res) { reset_error(); } diff --git a/include/sqlite.hpp b/include/sqlite.hpp index fa6a8bb4..635a6e07 100644 --- a/include/sqlite.hpp +++ b/include/sqlite.hpp @@ -218,6 +218,9 @@ class sqlite { // just support execute string sql without placeholders bool execute(const std::string &sql) { +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif int result = sqlite3_prepare_v2(handle_, sql.data(), (int)sql.size(), &stmt_, nullptr); if (result != SQLITE_OK) { diff --git a/include/utility.hpp b/include/utility.hpp index 7c70da79..1ba3fa53 100644 --- a/include/utility.hpp +++ b/include/utility.hpp @@ -188,7 +188,7 @@ inline std::string generate_auto_insert_sql( int index = 0; std::string fields = "("; - std::string values = " values("; + std::string values = "values("; auto it = auto_key_map_.find(name.data()); for (auto i = 0; i < SIZE; ++i) { std::string field_name = iguana::get_name(i).data(); diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 70c35868..1662ca09 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1014,12 +1014,12 @@ TEST_CASE("create table with unique") { mysql.execute("drop table if exists person"); mysql.create_datatable(ormpp_auto_key{"id"}, ormpp_unique{{"name", "age"}}); - mysql.insert({"purecpp", 200}); + mysql.insert({"purecpp"}); auto vec1 = mysql.query("order by id"); auto vec2 = mysql.query("limit 1"); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); - mysql.insert({"purecpp", 200}); + mysql.insert({"purecpp"}); auto vec3 = mysql.query(); CHECK(vec3.size() == 1); } @@ -1031,12 +1031,12 @@ TEST_CASE("create table with unique") { sqlite.execute("drop table if exists person"); sqlite.create_datatable(ormpp_auto_key{"id"}, ormpp_unique{{"name", "age"}}); - sqlite.insert({"purecpp", 200}); + sqlite.insert({"purecpp"}); auto vec1 = sqlite.query("order by id"); auto vec2 = sqlite.query("limit 1"); CHECK(vec1.size() == 1); CHECK(vec2.size() == 1); - sqlite.insert({"purecpp", 200}); + sqlite.insert({"purecpp"}); auto vec3 = sqlite.query(); CHECK(vec3.size() == 1); } @@ -1049,9 +1049,9 @@ TEST_CASE("get insert id") { if (mysql.connect(ip, username, password, db)) { mysql.execute("drop table if exists person"); mysql.create_datatable(ormpp_auto_key{"id"}); - mysql.insert({"purecpp", 200}); - mysql.insert({"purecpp", 200}); - int id = mysql.insert({"purecpp", 200}, true); + mysql.insert({"purecpp"}); + mysql.insert({"purecpp"}); + int id = mysql.insert({"purecpp"}, true); CHECK(id == 3); } #endif @@ -1061,9 +1061,9 @@ TEST_CASE("get insert id") { if (sqlite.connect(db)) { sqlite.execute("drop table if exists person"); sqlite.create_datatable(ormpp_auto_key{"id"}); - sqlite.insert({"purecpp", 200}); - sqlite.insert({"purecpp", 200}); - int id = sqlite.insert({"purecpp", 200}, true); + sqlite.insert({"purecpp"}); + sqlite.insert({"purecpp"}); + int id = sqlite.insert({"purecpp"}, true); CHECK(id == 3); } #endif @@ -1073,10 +1073,10 @@ TEST_CASE("delete records") { #ifdef ORMPP_ENABLE_MYSQL dbng mysql; if (mysql.connect(ip, username, password, db)) { + mysql.execute("drop table if exists person"); mysql.create_datatable(ormpp_auto_key{"id"}); - mysql.delete_records(); - mysql.insert({"other", 200}); - mysql.insert({"purecpp", 200}); + mysql.insert({"other"}); + mysql.insert({"purecpp"}); mysql.delete_records("name = 'other';drop table person"); auto vec = mysql.query(); CHECK(vec.size() == 2); @@ -1085,10 +1085,10 @@ TEST_CASE("delete records") { #ifdef ORMPP_ENABLE_PG // dbng postgres; // if (postgres.connect(ip, username, password, db)) { + // postgres.execute("drop table if exists person"); // postgres.create_datatable(ormpp_auto_key{"id"}); - // postgres.delete_records(); - // postgres.insert({"other", 200}); - // postgres.insert({purecpp", 200}); + // postgres.insert({"other"}); + // postgres.insert({"purecpp"}); // postgres.delete_records("name = 'other';drop table person"); // auto vec = postgres.query(); // CHECK(vec.size() == 2); @@ -1097,10 +1097,10 @@ TEST_CASE("delete records") { #ifdef ORMPP_ENABLE_SQLITE3 dbng sqlite; if (sqlite.connect(db)) { + sqlite.execute("drop table if exists person"); sqlite.create_datatable(ormpp_auto_key{"id"}); - sqlite.delete_records(); - sqlite.insert({"other", 200}); - sqlite.insert({"purecpp", 200}); + sqlite.insert({"other"}); + sqlite.insert({"purecpp"}); sqlite.delete_records("name = 'other';drop table person"); auto vec = sqlite.query(); CHECK(vec.size() == 1); @@ -1138,3 +1138,22 @@ TEST_CASE("alias") { } #endif } + +#ifdef ORMPP_ENABLE_PG +TEST_CASE("pg update") { + dbng postgres; + if (postgres.connect(ip, username, password, db)) { + postgres.execute("drop table if exists person"); + postgres.create_datatable(ormpp_auto_key{"id"}); + postgres.insert({"purecpp"}); + auto vec1 = postgres.query(); + CHECK(vec1.size() == 1); + vec1.front().name = "other"; + postgres.update(vec1.front()); + auto vec2 = postgres.query(); + CHECK(vec2.size() == 1); + CHECK(vec2.front().name == "other"); + CHECK(vec1.front().id == vec2.front().id); + } +} +#endif \ No newline at end of file