From 2b2c4736ffe7bbce09064ec07494c0d9c5ab4351 Mon Sep 17 00:00:00 2001 From: Jacyking <791026912@qq.com> Date: Tue, 20 Feb 2024 14:20:04 +0800 Subject: [PATCH] add query_s delete_records_s --- ormpp/dbng.hpp | 16 ++--- ormpp/mysql.hpp | 147 ++++++++++++++++++++++++++++++++++++++++--- ormpp/postgresql.hpp | 78 +++++++++++++++++++---- ormpp/sqlite.hpp | 85 +++++++++++++++++++++---- ormpp/utility.hpp | 24 ++++--- tests/test_ormpp.cpp | 143 ++++++++++++++++++++++++++--------------- 6 files changed, 394 insertions(+), 99 deletions(-) diff --git a/ormpp/dbng.hpp b/ormpp/dbng.hpp index ebba9b2a..cd232087 100644 --- a/ormpp/dbng.hpp +++ b/ormpp/dbng.hpp @@ -74,21 +74,23 @@ class dbng { } template - bool delete_records(Args &&...where_condition) { - return db_.template delete_records( - std::forward(where_condition)...); + bool delete_records_s(const std::string &str = "", Args &&...args) { + return db_.template delete_records_s(str, std::forward(args)...); } template - bool delete_records0(const std::string &str = "", Args &&...args) { - return db_.template delete_records0(str, std::forward(args)...); + std::vector query_s(const std::string &str = "", Args &&...args) { + return db_.template query_s(str, std::forward(args)...); } + // deprecated template - std::vector query0(const std::string &str = "", Args &&...args) { - return db_.template query0(str, std::forward(args)...); + bool delete_records(Args &&...where_condition) { + return db_.template delete_records( + std::forward(where_condition)...); } + // deprecated // restriction, all the args are string, the first is the where condition, // rest are append conditions template diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index d99366d0..0e66f731 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -279,11 +279,8 @@ class mysql { } template - bool delete_records0(const std::string &str, Args &&...args) { - auto sql = generate_delete_sql(); - if (!str.empty()) { - sql += "where " + str; - } + bool delete_records_s(const std::string &str, Args &&...args) { + auto sql = generate_delete_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -318,12 +315,9 @@ class mysql { } template - std::enable_if_t, std::vector> query0( + std::enable_if_t, std::vector> query_s( const std::string &str, Args &&...args) { - std::string sql = generate_query_sql(); - if (!str.empty()) { - sql += "where " + str; - } + std::string sql = generate_query_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -407,6 +401,139 @@ class mysql { return v; } + template + std::enable_if_t, std::vector> query_s( + const std::string &sql, Args &&...args) { + static_assert(iguana::is_tuple::value); + constexpr auto SIZE = std::tuple_size_v; +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif + stmt_ = mysql_stmt_init(con_); + if (!stmt_) { + set_last_error(mysql_error(con_)); + return {}; + } + + auto guard = guard_statment(stmt_); + + if (mysql_stmt_prepare(stmt_, sql.c_str(), (int)sql.size())) { + set_last_error(mysql_stmt_error(stmt_)); + return {}; + } + + if constexpr (sizeof...(Args) > 0) { + size_t index = 0; + using expander = int[]; + std::vector param_binds; + expander{0, (set_param_bind(param_binds, args), 0)...}; + if (mysql_stmt_bind_param(stmt_, ¶m_binds[0])) { + set_last_error(mysql_stmt_error(stmt_)); + return {}; + } + } + + std::array().is_null), + result_size::value> + nulls = {}; + std::array::value> param_binds = {}; + std::map> mp; + + std::vector v; + T tp{}; + + int index = 0; + iguana::for_each( + tp, + [¶m_binds, &index, &nulls, &mp, &tp, this](auto &t, auto /*i*/) { + using U = std::remove_reference_t; + if constexpr (iguana::is_reflection_v) { + iguana::for_each(t, [¶m_binds, &index, &nulls, &mp, &t, this]( + auto &item, auto /*i*/) { + set_param_bind(param_binds[index], t.*item, index, mp, + nulls[index]); + index++; + }); + } + else { + set_param_bind(param_binds[index], t, index, mp, nulls[index]); + index++; + } + }, + std::make_index_sequence>{}); + + if (index == 0) { + return {}; + } + + if (mysql_stmt_bind_result(stmt_, ¶m_binds[0])) { + set_last_error(mysql_stmt_error(stmt_)); + return {}; + } + + if (mysql_stmt_execute(stmt_)) { + set_last_error(mysql_stmt_error(stmt_)); + return {}; + } + + while (mysql_stmt_fetch(stmt_) == 0) { + index = 0; + iguana::for_each( + tp, + [¶m_binds, &index, &mp, &tp, this](auto &t, auto /*i*/) { + using U = std::remove_reference_t; + if constexpr (iguana::is_reflection_v) { + iguana::for_each(t, [¶m_binds, &index, &mp, &t, this]( + auto ele, auto /*i*/) { + set_value(param_binds.at(index), t.*ele, index, mp); + index++; + }); + } + else { + set_value(param_binds.at(index), t, index, mp); + index++; + } + }, + std::make_index_sequence{}); + + for (auto &p : mp) { + p.second.assign(p.second.size(), 0); + } + + index = 0; + iguana::for_each( + tp, + [&index, nulls, &tp](auto &t, auto /*i*/) { + using U = std::remove_reference_t; + if constexpr (iguana::is_reflection_v) { + iguana::for_each(t, [&index, nulls, &t](auto ele, auto /*i*/) { + if (nulls.at(index++)) { + using W = + std::remove_reference_t().*ele)>; + if constexpr (is_optional_v::value || + std::is_arithmetic_v) { + t.*ele = {}; + } + } + }); + } + else { + if (nulls.at(index++)) { + if constexpr (is_optional_v::value || + std::is_arithmetic_v) { + t = {}; + } + } + } + }, + std::make_index_sequence{}); + + v.push_back(std::move(tp)); + } + + return v; + } + // if there is a sql error, how to tell the user? throw exception? template std::enable_if_t, std::vector> query( diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index 1330b063..b7abd6eb 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -125,11 +125,8 @@ class postgresql { } template - bool delete_records0(const std::string &str, Args &&...args) { - auto sql = generate_delete_sql(); - if (!str.empty()) { - sql += "where " + str; - } + bool delete_records_s(const std::string &str, Args &&...args) { + auto sql = generate_delete_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -160,12 +157,9 @@ class postgresql { } template - std::enable_if_t, std::vector> query0( + std::enable_if_t, std::vector> query_s( const std::string &str, Args &&...args) { - std::string sql = generate_query_sql(); - if (!str.empty()) { - sql += "where " + str; - } + std::string sql = generate_query_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -207,11 +201,73 @@ class postgresql { return v; } + template + constexpr std::enable_if_t, std::vector> + query_s(const std::string &sql, Args &&...args) { + static_assert(iguana::is_tuple::value); +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif + if (!prepare(sql)) + return {}; + + if constexpr (sizeof...(Args) > 0) { + size_t index = 0; + using expander = int[]; + std::vector param_values_buf; + std::vector> param_values; + expander{0, (set_param_values(param_values, args), 0)...}; + for (auto &item : param_values) { + param_values_buf.push_back(item.data()); + } + res_ = PQexecPrepared(con_, "", (int)param_values.size(), + param_values_buf.data(), NULL, NULL, 0); + } + else { + res_ = PQexec(con_, sql.data()); + } + + auto guard = guard_statment(res_); + if (PQresultStatus(res_) != PGRES_TUPLES_OK) { + return {}; + } + + std::vector v; + auto ntuples = PQntuples(res_); + + for (auto i = 0; i < ntuples; i++) { + T tp = {}; + int index = 0; + iguana::for_each( + tp, + [this, i, &index](auto &item, auto I) { + if constexpr (iguana::is_reflection_v) { + std::remove_reference_t t = {}; + iguana::for_each(t, [this, &index, &t, i](auto ele, auto /*i*/) { + assign(t.*ele, (int)i, index++); + }); + item = std::move(t); + } + else { + assign(item, (int)i, index++); + } + }, + std::make_index_sequence>{}); + + if (index > 0) + v.push_back(std::move(tp)); + } + + return v; + } + template std::enable_if_t, std::vector> query( Args &&...args) { std::string sql = generate_query_sql(args...); - +#ifdef ORMPP_ENABLE_LOG + std::cout << sql << std::endl; +#endif if (!prepare(sql)) return {}; diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index e211f7ab..523e1f78 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -122,11 +122,8 @@ class sqlite { } template - bool delete_records0(const std::string &str, Args &&...args) { - auto sql = generate_delete_sql(); - if (!str.empty()) { - sql += "where " + str; - } + bool delete_records_s(const std::string &str, Args &&...args) { + auto sql = generate_delete_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -152,12 +149,9 @@ class sqlite { } template - std::enable_if_t, std::vector> query0( + std::enable_if_t, std::vector> query_s( const std::string &str, Args &&...args) { - std::string sql = generate_query_sql(); - if (!str.empty()) { - sql += "where " + str; - } + std::string sql = generate_query_sql(str); #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif @@ -196,6 +190,62 @@ class sqlite { return v; } + template + std::enable_if_t, std::vector> query_s( + const std::string &sql, Args &&...args) { + static_assert(iguana::is_tuple::value); +#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) { + set_last_error(sqlite3_errmsg(handle_)); + return {}; + } + + if constexpr (sizeof...(Args) > 0) { + size_t index = 0; + using expander = int[]; + expander{0, (set_param_bind(args, ++index), 0)...}; + } + + auto guard = guard_statment(stmt_); + + std::vector v; + while (true) { + result = sqlite3_step(stmt_); + if (result == SQLITE_DONE) + break; + + if (result != SQLITE_ROW) + break; + + T tp = {}; + int index = 0; + iguana::for_each( + tp, + [this, &index](auto &item, auto /*I*/) { + if constexpr (iguana::is_reflection_v) { + std::remove_reference_t t = {}; + iguana::for_each(t, [this, &index, &t](auto ele, auto /*i*/) { + assign(t.*ele, index++); + }); + item = std::move(t); + } + else { + assign(item, index++); + } + }, + std::make_index_sequence>{}); + + if (index > 0) + v.push_back(std::move(tp)); + } + + return v; + } + // restriction, all the args are string, the first is the where condition, // rest are append conditions template @@ -517,6 +567,12 @@ class sqlite { return SQLITE_OK == sqlite3_bind_text(stmt_, i, value, sizeof(U), nullptr); } +#ifdef ORMPP_WITH_CSTRING + else if constexpr (std::is_same_v) { + return SQLITE_OK == sqlite3_bind_text(stmt_, i, value.GetString(), + (int)value.GetLength(), nullptr); + } +#endif else { static_assert(!sizeof(U), "this type has not supported yet"); } @@ -524,11 +580,11 @@ class sqlite { template void assign(T &&value, int i) { + using U = std::remove_const_t>; if (sqlite3_column_type(stmt_, i) == SQLITE_NULL) { - value = {}; + value = U{}; return; } - using U = std::remove_const_t>; if constexpr (is_optional_v::value) { using value_type = typename U::value_type; value_type item; @@ -560,6 +616,11 @@ class sqlite { else if constexpr (is_char_array_v) { memcpy(value, sqlite3_column_text(stmt_, i), sizeof(U)); } +#ifdef ORMPP_WITH_CSTRING + else if constexpr (std::is_same_v) { + value.SetString((const char *)sqlite3_column_text(stmt_, i)); + } +#endif else { static_assert(!sizeof(U), "this type has not supported yet"); } diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index 1455b805..6c10142b 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -162,6 +162,11 @@ inline constexpr auto get_type_names(DBType type) { else if constexpr (is_optional_v::value) { s = ormpp_sqlite::type_to_name(identity{}); } +#ifdef ORMPP_WITH_CSTRING + else if constexpr (std::is_same_v) { + s = "TEXT"sv; + } +#endif else { s = ormpp_sqlite::type_to_name(identity{}); } @@ -402,8 +407,8 @@ inline std::string generate_delete_sql(Args &&...where_conditon) { auto name = get_name(); append(sql, name.data()); if constexpr (sizeof...(Args) > 0) { - if (!is_empty(std::forward(where_conditon)...)) // fix for vs2017 - append(sql, " where ", std::forward(where_conditon)...); + if (!is_empty(std::forward(where_conditon)...)) + append(sql, "where", std::forward(where_conditon)...); } return sql; } @@ -441,18 +446,17 @@ inline void get_sql_conditions(std::string &sql, const std::string &arg, template inline std::string generate_query_sql(Args &&...args) { - constexpr size_t param_size = sizeof...(Args); - static_assert(param_size == 0 || param_size > 0); - std::string sql = "select "; + bool where = true; auto name = get_name(); + std::string sql = "select "; auto fields = get_fields(); append(sql, fields.data(), "from", name.data()); - - std::string where_sql = ""; - if constexpr (param_size > 0) { - where_sql = " where 1=1 and "; + if constexpr (sizeof...(Args) > 0) { + if (where && !is_empty(std::forward(args)...)) { + append(sql, "where 1=1 and "); + where = false; + } } - sql.append(where_sql); get_sql_conditions(sql, std::forward(args)...); return sql; } diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index aa25349b..4132ccec 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1199,7 +1199,7 @@ TEST_CASE("get insert id after insert") { #endif } -TEST_CASE("query0 delete_records0") { +TEST_CASE("query_s delete_records_s") { #ifdef ORMPP_ENABLE_MYSQL dbng mysql; if (mysql.connect(ip, username, password, db)) { @@ -1207,26 +1207,41 @@ TEST_CASE("query0 delete_records0") { mysql.create_datatable(ormpp_auto_key{"id"}); mysql.insert({"other"}); mysql.insert({"purecpp", 200}); - auto vec1 = mysql.query0(); - auto vec2 = mysql.query0("name=?", "purecpp"); - auto vec3 = mysql.query0("name=?", std::string("purecpp")); - auto vec4 = mysql.query0("name=? and age=?", "purecpp", 200); - auto vec5 = mysql.query0("name=?", "purecpp or 1=1"); - mysql.delete_records0("name=?", "purecpp or 1=1"); - auto vec6 = mysql.query0(); - mysql.delete_records0("name=?", "purecpp"); - auto vec7 = mysql.query0(); - mysql.delete_records0(); - auto vec8 = mysql.query0(); + auto v_tp1 = mysql.query_s>( + "select * from person where name=?", "purecpp"); + auto v_tp2 = mysql.query_s>( + "select * from person where name=?", "purecpp' or '1=1"); + auto v_tp3 = mysql.query_s>("select * from person"); + auto vec1 = mysql.query_s(); + auto vec2 = mysql.query_s("name=?", "purecpp"); + auto vec3 = mysql.query_s("select * from person"); + auto vec4 = mysql.query_s("name=?", std::string("purecpp")); + auto vec5 = mysql.query_s("name=? and age=?", "purecpp", 200); + auto vec6 = + mysql.query_s("select * from person where name=?", "purecpp"); + auto vec7 = mysql.query_s("name=?", "purecpp' or '1=1"); + mysql.delete_records_s("name=?", "purecpp' or '1=1"); + auto vec8 = mysql.query_s(); + mysql.delete_records_s("name=?", "purecpp"); + auto vec9 = mysql.query_s(); + mysql.delete_records_s(); + auto vec10 = mysql.query_s(); CHECK(vec1.front().name == "other"); CHECK(vec1.back().name == "purecpp"); + CHECK(vec3.front().name == "other"); + CHECK(vec3.back().name == "purecpp"); + CHECK(std::get<0>(v_tp3.front()).name == "other"); + CHECK(std::get<0>(v_tp3.back()).name == "purecpp"); CHECK(vec2.front().age == 200); - CHECK(vec3.front().age == 200); CHECK(vec4.front().age == 200); - CHECK(vec5.size() == 0); - CHECK(vec6.size() == 2); - CHECK(vec7.size() == 1); - CHECK(vec8.size() == 0); + CHECK(vec5.front().age == 200); + CHECK(vec6.front().age == 200); + CHECK(std::get<0>(v_tp1.front()).age == 200); + CHECK(v_tp2.size() == 0); + CHECK(vec7.size() == 0); + CHECK(vec8.size() == 2); + CHECK(vec9.size() == 1); + CHECK(vec10.size() == 0); } #endif #ifdef ORMPP_ENABLE_PG @@ -1236,26 +1251,41 @@ TEST_CASE("query0 delete_records0") { postgres.create_datatable(ormpp_auto_key{"id"}); postgres.insert({"other"}); postgres.insert({"purecpp", 200}); - auto vec1 = postgres.query0(); - auto vec2 = postgres.query0("name=$1", "purecpp"); - auto vec3 = postgres.query0("name=$1", std::string("purecpp")); - auto vec4 = postgres.query0("name=$1 and age=$2", "purecpp", 200); - auto vec5 = postgres.query0("name=$1", "purecpp or 1=1"); - postgres.delete_records0("name=$1", "purecpp or 1=1"); - auto vec6 = postgres.query0(); - postgres.delete_records0("name=$1", "purecpp"); - auto vec7 = postgres.query0(); - postgres.delete_records0(); - auto vec8 = postgres.query0(); + auto v_tp1 = postgres.query_s>( + "select * from person where name=$1", "purecpp"); + auto v_tp2 = postgres.query_s>( + "select * from person where name=$1", "purecpp' or '1=1"); + auto v_tp3 = postgres.query_s>("select * from person"); + auto vec1 = postgres.query_s(); + auto vec2 = postgres.query_s("name=$1", "purecpp"); + auto vec3 = postgres.query_s("select * from person"); + auto vec4 = postgres.query_s("name=$1", std::string("purecpp")); + auto vec5 = postgres.query_s("name=$1 and age=$2", "purecpp", 200); + auto vec6 = postgres.query_s("select * from person where name=$1", + "purecpp"); + auto vec7 = postgres.query_s("name=$1", "purecpp' or '1=1"); + postgres.delete_records_s("name=$1", "purecpp' or '1=1"); + auto vec8 = postgres.query_s(); + postgres.delete_records_s("name=$1", "purecpp"); + auto vec9 = postgres.query_s(); + postgres.delete_records_s(); + auto vec10 = postgres.query_s(); CHECK(vec1.front().name == "other"); CHECK(vec1.back().name == "purecpp"); + CHECK(vec3.front().name == "other"); + CHECK(vec3.back().name == "purecpp"); + CHECK(std::get<0>(v_tp3.front()).name == "other"); + CHECK(std::get<0>(v_tp3.back()).name == "purecpp"); CHECK(vec2.front().age == 200); - CHECK(vec3.front().age == 200); CHECK(vec4.front().age == 200); - CHECK(vec5.size() == 0); - CHECK(vec6.size() == 2); - CHECK(vec7.size() == 1); - CHECK(vec8.size() == 0); + CHECK(vec5.front().age == 200); + CHECK(vec6.front().age == 200); + CHECK(std::get<0>(v_tp1.front()).age == 200); + CHECK(v_tp2.size() == 0); + CHECK(vec7.size() == 0); + CHECK(vec8.size() == 2); + CHECK(vec9.size() == 1); + CHECK(vec10.size() == 0); } #endif #ifdef ORMPP_ENABLE_SQLITE3 @@ -1265,26 +1295,41 @@ TEST_CASE("query0 delete_records0") { sqlite.create_datatable(ormpp_auto_key{"id"}); sqlite.insert({"other"}); sqlite.insert({"purecpp", 200}); - auto vec1 = sqlite.query0(); - auto vec2 = sqlite.query0("name=?", "purecpp"); - auto vec3 = sqlite.query0("name=?", std::string("purecpp")); - auto vec4 = sqlite.query0("name=? and age=?", "purecpp", 200); - auto vec5 = sqlite.query0("name=?", "purecpp or 1=1"); - sqlite.delete_records0("name=?", "purecpp or 1=1"); - auto vec6 = sqlite.query0(); - sqlite.delete_records0("name=?", "purecpp"); - auto vec7 = sqlite.query0(); - sqlite.delete_records0(); - auto vec8 = sqlite.query0(); + auto v_tp1 = sqlite.query_s>( + "select * from person where name=?", "purecpp"); + auto v_tp2 = sqlite.query_s>( + "select * from person where name=?", "purecpp' or '1=1"); + auto v_tp3 = sqlite.query_s>("select * from person"); + auto vec1 = sqlite.query_s(); + auto vec2 = sqlite.query_s("name=?", "purecpp"); + auto vec3 = sqlite.query_s("select * from person"); + auto vec4 = sqlite.query_s("name=?", std::string("purecpp")); + auto vec5 = sqlite.query_s("name=? and age=?", "purecpp", 200); + auto vec6 = + sqlite.query_s("select * from person where name=?", "purecpp"); + auto vec7 = sqlite.query_s("name=?", "purecpp' or '1=1"); + sqlite.delete_records_s("name=?", "purecpp' or '1=1"); + auto vec8 = sqlite.query_s(); + sqlite.delete_records_s("name=?", "purecpp"); + auto vec9 = sqlite.query_s(); + sqlite.delete_records_s(); + auto vec10 = sqlite.query_s(); CHECK(vec1.front().name == "other"); CHECK(vec1.back().name == "purecpp"); + CHECK(vec3.front().name == "other"); + CHECK(vec3.back().name == "purecpp"); + CHECK(std::get<0>(v_tp3.front()).name == "other"); + CHECK(std::get<0>(v_tp3.back()).name == "purecpp"); CHECK(vec2.front().age == 200); - CHECK(vec3.front().age == 200); CHECK(vec4.front().age == 200); - CHECK(vec5.size() == 0); - CHECK(vec6.size() == 2); - CHECK(vec7.size() == 1); - CHECK(vec8.size() == 0); + CHECK(vec5.front().age == 200); + CHECK(vec6.front().age == 200); + CHECK(std::get<0>(v_tp1.front()).age == 200); + CHECK(v_tp2.size() == 0); + CHECK(vec7.size() == 0); + CHECK(vec8.size() == 2); + CHECK(vec9.size() == 1); + CHECK(vec10.size() == 0); } #endif }