From e7f4615fc722b8a97d1998b4e9a2f08e66a8b4d6 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 20 Aug 2026 22:47:05 +0800 Subject: [PATCH 1/5] fix mysql keyword fields and insert defaults --- README.md | 16 +++++++++++++ ormpp/mysql.hpp | 20 ++++++++++------ ormpp/mysql_async.hpp | 17 +++++++++----- ormpp/postgresql.hpp | 3 ++- ormpp/sqlite.hpp | 4 +++- ormpp/utility.hpp | 52 ++++++++++++++++++++++++++++++++++++++---- tests/test_ormpp.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 146 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 6809b8a9..f6e50b37 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,22 @@ struct person { REGISTER_AUTO_KEY(person, id) ``` +## 跳过插入字段 + +使用REGISTER_SKIP_INSERT_FIELD注册插入时需要跳过的字段,适用于让数据库默认值生效的列,例如 MySQL 的 `timestamp NOT NULL DEFAULT current_timestamp()`。 + +```C++ +struct material { + int id; + std::string datetime; + std::string company; +}; +REGISTER_AUTO_KEY(material, id) +REGISTER_SKIP_INSERT_FIELD(material, datetime) +``` + +上例在执行 `insert` 时会省略 `datetime` 字段,数据库会使用表定义中的默认值。`replace` 和 `update` 不受影响。 + ## 冲突主键 使用REGISTER_CONFLICT_KEY注册冲突主键来进行update,如果未注册冲突主键则会采用自增主键 diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index 24b383aa..5e36fceb 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -1022,13 +1022,13 @@ class mysql { if (item.fields.size() > 1) { std::string str; for (auto &name : item.fields) { - str.append(name).append(","); + str.append(quote_mysql_identifier(name)).append(","); } str.pop_back(); unique.insert(str); } else { - unique.insert(*item.fields.begin()); + unique.insert(quote_mysql_identifier(*item.fields.begin())); } } }); @@ -1042,7 +1042,9 @@ class mysql { T t; ylt::reflection::for_each(t, [&](auto &field, auto name, size_t index) { using item_type = std::decay_t; - sql.append(name).append(" ").append(type_name_arr[index]); + sql.append(quote_mysql_identifier(name)) + .append(" ") + .append(type_name_arr[index]); std::string str_name(name); @@ -1061,12 +1063,14 @@ class mysql { }); if (!auto_key.empty()) { - sql.append("PRIMARY KEY (").append(auto_key).append("),"); + sql.append("PRIMARY KEY (") + .append(quote_mysql_identifier(auto_key)) + .append("),"); } else if (!primary_keys.empty()) { sql.append("PRIMARY KEY ("); for (auto key : primary_keys) { - sql.append(key).append(","); + sql.append(quote_mysql_identifier(key)).append(","); } sql.pop_back(); sql.append("),"); @@ -1094,7 +1098,8 @@ class mysql { else { ylt::reflection::for_each(t, [arr, ¶m_binds, type, this]( auto &field, auto name, auto index) { - if (type == OptType::insert && is_auto_key(name)) { + if (type == OptType::insert && + (is_auto_key(name) || is_skip_insert_field(name))) { return; } if constexpr (sizeof...(members) > 0) { @@ -1124,7 +1129,8 @@ class mysql { } } - if (mysql_stmt_bind_param(stmt_, ¶m_binds[0])) { + if (!param_binds.empty() && + mysql_stmt_bind_param(stmt_, param_binds.data())) { set_last_error(mysql_stmt_error(stmt_)); return INT_MIN; } diff --git a/ormpp/mysql_async.hpp b/ormpp/mysql_async.hpp index 69b47fc1..de25ade2 100644 --- a/ormpp/mysql_async.hpp +++ b/ormpp/mysql_async.hpp @@ -1008,13 +1008,13 @@ std::string generate_create_table_sql(DBType db_type, bool append_mysql_charset, if (item.fields.size() > 1) { std::string names; for (auto& name : item.fields) { - names.append(name).append(","); + names.append(quote_mysql_identifier(name)).append(","); } names.pop_back(); unique.insert(std::move(names)); } else if (!item.fields.empty()) { - unique.insert(*item.fields.begin()); + unique.insert(quote_mysql_identifier(*item.fields.begin())); } } }, @@ -1030,7 +1030,9 @@ std::string generate_create_table_sql(DBType db_type, bool append_mysql_charset, T sample{}; ylt::reflection::for_each(sample, [&](auto& /*field*/, auto name, size_t index) { - sql.append(name).append(" ").append(type_name_arr[index]); + sql.append(quote_mysql_identifier(name)) + .append(" ") + .append(type_name_arr[index]); std::string str_name(name); if (!auto_primary_key.empty() && @@ -1047,12 +1049,14 @@ std::string generate_create_table_sql(DBType db_type, bool append_mysql_charset, }); if (!auto_key.empty()) { - sql.append("PRIMARY KEY (").append(auto_key).append("),"); + sql.append("PRIMARY KEY (") + .append(quote_mysql_identifier(auto_key)) + .append("),"); } else if (!primary_keys.empty()) { sql.append("PRIMARY KEY ("); for (const auto& key : primary_keys) { - sql.append(key).append(","); + sql.append(quote_mysql_identifier(key)).append(","); } sql.back() = ')'; sql.push_back(','); @@ -1849,7 +1853,8 @@ class mysql_async { ylt::reflection::for_each( value, [&](auto& field, auto name, auto /*index*/) { - if (type == OptType::insert && is_auto_key(name)) { + if (type == OptType::insert && + (is_auto_key(name) || is_skip_insert_field(name))) { return; } values.push_back(detail::mysql_async::to_query_arg_impl( diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index 6b4594ea..50b92fb5 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -575,7 +575,8 @@ class postgresql { else { ylt::reflection::for_each(t, [arr, ¶m_values, type, this]( auto &field, auto name, auto index) { - if (type == OptType::insert && is_auto_key(name)) { + if (type == OptType::insert && + (is_auto_key(name) || is_skip_insert_field(name))) { return; } if constexpr (sizeof...(members) > 0) { diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index dd9e3cab..c9823e74 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -596,7 +596,9 @@ class sqlite { else { ylt::reflection::for_each(t, [&bind_ok, &index, type, this]( auto &field, auto name, auto /*index*/) { - if ((type == OptType::insert && is_auto_key(name)) || !bind_ok) { + if ((type == OptType::insert && + (is_auto_key(name) || is_skip_insert_field(name))) || + !bind_ok) { return; } bind_ok = set_param_bind(field, ++index); diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index 786161de..1bf000c5 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -7,7 +7,9 @@ #include #include #include +#include #include +#include #include "entity.hpp" #include "iguana/util.hpp" @@ -72,6 +74,42 @@ inline auto is_auto_key(std::string_view field_name) { return it == get_auto_key_map().end() ? false : it->second == field_name; } +inline auto &get_skip_insert_field_map() { + static std::unordered_map> map; + return map; +} + +inline int add_skip_insert_field(std::string_view key, std::string_view value) { + get_skip_insert_field_map()[key].insert(value); + return 0; +} + +template +inline auto is_skip_insert_field(std::string_view field_name) { + auto it = get_skip_insert_field_map().find(get_short_struct_name()); + return it == get_skip_insert_field_map().end() + ? false + : it->second.find(field_name) != it->second.end(); +} + +inline std::string quote_mysql_identifier(std::string_view name) { + if (name.size() >= 2 && name.front() == '`' && name.back() == '`') { + return std::string(name); + } + + std::string result; + result.reserve(name.size() + 2); + result.push_back('`'); + for (char ch : name) { + if (ch == '`') { + result.push_back('`'); + } + result.push_back(ch); + } + result.push_back('`'); + return result; +} + #ifdef _MSC_VER #define ORMPP_UNIQUE_VARIABLE(str) YLT_CONCAT(str, __COUNTER__) #else @@ -82,6 +120,11 @@ inline auto is_auto_key(std::string_view field_name) { inline auto ORMPP_UNIQUE_VARIABLE(STRUCT_NAME) = ormpp::add_auto_key_field( \ ylt::reflection::get_struct_name(), #KEY); +#define REGISTER_SKIP_INSERT_FIELD(STRUCT_NAME, FIELD) \ + inline auto ORMPP_UNIQUE_VARIABLE(STRUCT_NAME) = \ + ormpp::add_skip_insert_field( \ + ylt::reflection::get_struct_name(), #FIELD); + inline auto &get_conflict_map() { static std::unordered_map map; return map; @@ -305,7 +348,7 @@ inline std::string get_fields(DBType db_type) { std::call_once(flag, [&fields, db_type]() { for (const auto &it : ylt::reflection::get_member_names()) { if (db_type == DBType::mysql) { - fields += "`" + std::string(it) + "`"; + fields += quote_mysql_identifier(it); } else { fields += std::string(it); @@ -398,7 +441,7 @@ inline std::vector get_conflict_keys(DBType db_type) { for (auto sv : v) { std::string str; if (db_type == DBType::mysql) { - str.append("`").append(sv).append("`"); + str = quote_mysql_identifier(sv); } else { str.append(sv); @@ -470,7 +513,8 @@ inline std::string generate_insert_sql(DBType db_type, bool insert, std::string values = "values("; for (size_t i = 0; i < Count; ++i) { std::string field_name(ylt::reflection::name_of(i)); - if (insert && is_auto_key(field_name)) { + if (insert && + (is_auto_key(field_name) || is_skip_insert_field(field_name))) { continue; } if (db_type == DBType::postgresql) { @@ -480,7 +524,7 @@ inline std::string generate_insert_sql(DBType db_type, bool insert, values += "?"; } if (db_type == DBType::mysql) { - fields += "`" + field_name + "`"; + fields += quote_mysql_identifier(field_name); } else { fields += field_name; diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 6af3ac3c..0d9d88b1 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -80,6 +80,22 @@ struct builder_person { }; REGISTER_AUTO_KEY(builder_person, id) +struct mysql_keyword_material_index { + int id; + std::string company; + std::string group; + int MaterialNumber; +}; +REGISTER_AUTO_KEY(mysql_keyword_material_index, id) + +struct insert_default_field { + int id; + std::string datetime; + std::string company; +}; +REGISTER_AUTO_KEY(insert_default_field, id) +REGISTER_SKIP_INSERT_FIELD(insert_default_field, datetime) + struct fake_postgresql_db { static constexpr DBType db_type_v = DBType::postgresql; }; @@ -1349,6 +1365,43 @@ TEST_CASE("create table") { #endif } +TEST_CASE("create mysql table with keyword field") { +#ifdef ORMPP_ENABLE_MYSQL + dbng mysql; + if (mysql.connect(ip, username, password, db)) { + mysql.execute("drop table if exists mysql_keyword_material_index"); + REQUIRE(mysql.create_datatable( + ormpp_auto_key{"id"})); + } +#endif +} + +TEST_CASE("skip insert field") { + auto mysql_sql = + generate_insert_sql(DBType::mysql, true); + CHECK(mysql_sql.find("`id`") == std::string::npos); + CHECK(mysql_sql.find("`datetime`") == std::string::npos); + CHECK(mysql_sql.find("`company`") != std::string::npos); + CHECK(mysql_sql.find("values(?)") != std::string::npos); + + dbng sqlite; +#ifdef SQLITE_HAS_CODEC + REQUIRE(sqlite.connect(db, password)); +#else + REQUIRE(sqlite.connect(db)); +#endif + sqlite.execute("drop table if exists insert_default_field"); + REQUIRE(sqlite.execute( + "create table insert_default_field(id integer primary key autoincrement, " + "datetime text default 'db_default', company text)")); + REQUIRE(sqlite.insert({0, "ignored", "purecpp"}) == 1); + + auto rows = sqlite.query_s("order by id"); + REQUIRE(rows.size() == 1); + CHECK(rows[0].datetime == "db_default"); + CHECK(rows[0].company == "purecpp"); +} + TEST_CASE("insert query") { ormpp_key key{col_name(&student::code)}; ormpp_not_null not_null{{col_name(&student::code), col_name(&student::age)}}; From b675460d8f372bd345c69a38a28723f1b4064a70 Mon Sep 17 00:00:00 2001 From: qicosmos <6500147+qicosmos@users.noreply.github.com> Date: Sat, 3 Oct 2026 11:03:29 +0800 Subject: [PATCH 2/5] Address review of PostgreSQL prepares and identifier quoting Let PostgreSQL infer parameter types for prepared statements with omitted insert fields. Quote RETURNING identifiers, document registration timing and C++20 build requirements, and extend SQL generation tests. --- README.md | 3 ++- ormpp/postgresql.hpp | 29 ++++++++++++----------------- ormpp/utility.hpp | 14 ++++++++++++++ tests/async_mysql_smoke.cpp | 23 +++++++++++++++++++++++ tests/test_ormpp.cpp | 9 ++++++++- 5 files changed, 59 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 809259a7..d233fea4 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ REGISTER_SKIP_INSERT_FIELD(material, datetime) ``` 上例在执行 `insert` 时会始终省略 `datetime` 字段,数据库会使用表定义中的默认值。如果某次插入需要显式提供该字段,请使用自定义 SQL;`replace` 和 `update` 不受影响。 +请在启动并发数据库操作前完成字段注册;`REGISTER_SKIP_INSERT_FIELD` 与 `REGISTER_AUTO_KEY` 一样,面向静态初始化期使用。 ## 冲突主键 @@ -548,7 +549,7 @@ target_include_directories(ormpp INTERFACE ormpp ormpp/ormpp ${PGSQL_INCLUDE_DIR ### 编译器支持 -需要支持C++17的编译器, 要求的编译器版本:linux gcc7.2, clang4.0; windows >vs2017 update5 +当前 CMake 构建要求 C++20;请使用支持 C++20 协程的编译器,例如 GCC 10+、Clang 13+ 或 MSVC 2019 16.8+。 ### 数据库的安装 diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index 981136dc..90e40c9d 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -549,12 +549,13 @@ class postgresql { } template - bool prepare(const std::string &sql, - int parameter_count = ylt::reflection::members_count_v) { + bool prepare(const std::string &sql) { #ifdef ORMPP_ENABLE_LOG std::cout << sql << std::endl; #endif - res_ = PQprepare(con_, "", sql.data(), parameter_count, nullptr); + // Parameter types are inferred by PostgreSQL, so the count must not be + // based on reflected members: inserts can omit auto and default fields. + res_ = PQprepare(con_, "", sql.data(), 0, nullptr); auto guard = guard_statment(res_); return PQresultStatus(res_) == PGRES_COMMAND_OK; } @@ -671,13 +672,10 @@ class postgresql { OptType type, bool get_insert_id = false, Args &&...args) { - const int parameter_count = - type == OptType::insert && sql.ends_with("default values ") - ? 0 - : ylt::reflection::members_count_v; - if (!prepare( - get_insert_id ? sql + "returning " + get_auto_key().data() : sql, - parameter_count)) { + if (!prepare(get_insert_id + ? sql + "returning " + + quote_postgresql_identifier(get_auto_key()) + : sql)) { return std::nullopt; } @@ -694,13 +692,10 @@ class postgresql { return std::nullopt; } - const int parameter_count = - type == OptType::insert && sql.ends_with("default values ") - ? 0 - : ylt::reflection::members_count_v; - if (!prepare( - get_insert_id ? sql + "returning " + get_auto_key().data() : sql, - parameter_count)) { + if (!prepare(get_insert_id + ? sql + "returning " + + quote_postgresql_identifier(get_auto_key()) + : sql)) { return std::nullopt; } diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index 8a4d5aca..41282b82 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -118,6 +118,20 @@ inline std::string quote_mysql_identifier(std::string_view name) { return result; } +inline std::string quote_postgresql_identifier(std::string_view name) { + std::string result; + result.reserve(name.size() + 2); + result.push_back('"'); + for (char ch : name) { + if (ch == '"') { + result.push_back('"'); + } + result.push_back(ch); + } + result.push_back('"'); + return result; +} + #ifdef _MSC_VER #define ORMPP_UNIQUE_VARIABLE(str) YLT_CONCAT(str, __COUNTER__) #else diff --git a/tests/async_mysql_smoke.cpp b/tests/async_mysql_smoke.cpp index 1d6fc157..bcaed715 100644 --- a/tests/async_mysql_smoke.cpp +++ b/tests/async_mysql_smoke.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "dbng.hpp" @@ -30,6 +31,14 @@ struct async_person { REGISTER_AUTO_KEY(async_person, id) YLT_REFL(async_person, id, name, age) +struct async_keyword_unique_row { + int id; + std::string group; + std::string company; +}; +REGISTER_AUTO_KEY(async_keyword_unique_row, id) +YLT_REFL(async_keyword_unique_row, id, group, company) + enum class async_color { blue = 10, red = 15 }; enum async_fruit { apple, banana }; @@ -642,6 +651,20 @@ TEST_CASE("mysql async formats placeholders with question marks in values") { "mysql_async: tuple column count mismatch", std::runtime_error); } +TEST_CASE("mysql async quotes composite unique keys") { + auto sql = + detail::mysql_async::generate_create_table_sql( + DBType::mysql, true, + std::make_tuple(ormpp_unique{{"group", "company"}})); + CHECK(sql.find("`group` ") != std::string::npos); + CHECK(sql.find("UNIQUE (`company`,`group`)") != std::string::npos); + + auto quoted_sql = + detail::mysql_async::generate_create_table_sql( + DBType::mysql, true, std::make_tuple(ormpp_unique{{"`group`"}})); + CHECK(quoted_sql.find("UNIQUE (`group`)") != std::string::npos); +} + TEST_CASE("mysql async smoke") { asio::io_context ctx; auto fut = asio::co_spawn(ctx, run_async_mysql_smoke(), asio::use_future); diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 80e7256f..f2e50545 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1771,7 +1771,7 @@ TEST_CASE("create mysql table with keyword field") { if (mysql.connect(ip, username, password, db)) { mysql.execute("drop table if exists mysql_keyword_material_index"); REQUIRE(mysql.create_datatable( - ormpp_auto_key{"id"})); + ormpp_auto_key{"id"}, ormpp_unique{{"group", "company"}})); REQUIRE(mysql.insert( {0, "purecpp", "first", 42}) == 1); auto rows = mysql.query_s("`group`='first'"); @@ -1795,6 +1795,13 @@ TEST_CASE("skip insert field") { CHECK(mysql_sql.find("`datetime`") == std::string::npos); CHECK(mysql_sql.find("`company`") != std::string::npos); CHECK(mysql_sql.find("values(?)") != std::string::npos); + auto pg_sql = + generate_insert_sql(DBType::postgresql, true); + CHECK(pg_sql.find("datetime") == std::string::npos); + CHECK(pg_sql.find("$1") != std::string::npos); + CHECK(pg_sql.find("$2") == std::string::npos); + CHECK(quote_postgresql_identifier("group") == "\"group\""); + CHECK(quote_postgresql_identifier("a\"b") == "\"a\"\"b\""); dbng sqlite; #ifdef SQLITE_HAS_CODEC From 2a35ba6e7dd76d8f9e14123a1bf2c8be0c8b841d Mon Sep 17 00:00:00 2001 From: qicosmos <6500147+qicosmos@users.noreply.github.com> Date: Sat, 3 Oct 2026 11:26:01 +0800 Subject: [PATCH 3/5] Fix MySQL composite-key integration test types --- tests/test_ormpp.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index f2e50545..8c485487 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -84,7 +84,7 @@ REGISTER_AUTO_KEY(builder_person, id) struct mysql_keyword_material_index { int id; std::string company; - std::string group; + int group; int MaterialNumber; }; REGISTER_AUTO_KEY(mysql_keyword_material_index, id) @@ -1771,17 +1771,17 @@ TEST_CASE("create mysql table with keyword field") { if (mysql.connect(ip, username, password, db)) { mysql.execute("drop table if exists mysql_keyword_material_index"); REQUIRE(mysql.create_datatable( - ormpp_auto_key{"id"}, ormpp_unique{{"group", "company"}})); + ormpp_auto_key{"id"}, ormpp_unique{{"group", "MaterialNumber"}})); REQUIRE(mysql.insert( - {0, "purecpp", "first", 42}) == 1); - auto rows = mysql.query_s("`group`='first'"); + {0, "purecpp", 1, 42}) == 1); + auto rows = mysql.query_s("`group`=1"); REQUIRE(rows.size() == 1); CHECK(rows.front().MaterialNumber == 42); - rows.front().group = "second"; + rows.front().group = 2; REQUIRE(mysql.update_some<&mysql_keyword_material_index::group>( rows.front()) == 1); auto updated = - mysql.query_s("`group`='second'"); + mysql.query_s("`group`=2"); REQUIRE(updated.size() == 1); CHECK(updated.front().company == "purecpp"); } From f3a757b09879662cb9c82f49d792059d964760c8 Mon Sep 17 00:00:00 2001 From: qicosmos <6500147+qicosmos@users.noreply.github.com> Date: Sat, 3 Oct 2026 11:28:23 +0800 Subject: [PATCH 4/5] Format MySQL keyword integration test --- tests/test_ormpp.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 8c485487..849d631e 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -1772,16 +1772,15 @@ TEST_CASE("create mysql table with keyword field") { mysql.execute("drop table if exists mysql_keyword_material_index"); REQUIRE(mysql.create_datatable( ormpp_auto_key{"id"}, ormpp_unique{{"group", "MaterialNumber"}})); - REQUIRE(mysql.insert( - {0, "purecpp", 1, 42}) == 1); + REQUIRE(mysql.insert({0, "purecpp", 1, 42}) == + 1); auto rows = mysql.query_s("`group`=1"); REQUIRE(rows.size() == 1); CHECK(rows.front().MaterialNumber == 42); rows.front().group = 2; REQUIRE(mysql.update_some<&mysql_keyword_material_index::group>( rows.front()) == 1); - auto updated = - mysql.query_s("`group`=2"); + auto updated = mysql.query_s("`group`=2"); REQUIRE(updated.size() == 1); CHECK(updated.front().company == "purecpp"); } From 57788ffe2a3ff9ca5ed1c040aaa77ca822c029a0 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Mon, 5 Oct 2026 11:12:50 +0800 Subject: [PATCH 5/5] simplify skipped insert fields and test default-only rows --- ormpp/mysql.hpp | 3 +-- ormpp/mysql_async.hpp | 3 +-- ormpp/postgresql.hpp | 3 +-- ormpp/sqlite.hpp | 3 +-- ormpp/utility.hpp | 13 ++++++++----- tests/test_ormpp.cpp | 25 +++++++++++++++++++++---- 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/ormpp/mysql.hpp b/ormpp/mysql.hpp index 66e780bf..4f74bda5 100644 --- a/ormpp/mysql.hpp +++ b/ormpp/mysql.hpp @@ -1463,8 +1463,7 @@ class mysql { t, [arr, ¶m_binds, &text_param_storage, &blob_param_storage, type, ¶m_length_storage, ¶m_null_storage, this](auto &field, auto name, auto index) { - if (type == OptType::insert && - (is_auto_key(name) || is_skip_insert_field(name))) { + if (type == OptType::insert && is_insert_skipped(name)) { return; } if constexpr (sizeof...(members) > 0) { diff --git a/ormpp/mysql_async.hpp b/ormpp/mysql_async.hpp index b0a4b6cc..eefc9d55 100644 --- a/ormpp/mysql_async.hpp +++ b/ormpp/mysql_async.hpp @@ -1860,8 +1860,7 @@ class mysql_async { ylt::reflection::for_each( value, [&](auto& field, auto name, auto /*index*/) { - if (type == OptType::insert && - (is_auto_key(name) || is_skip_insert_field(name))) { + if (type == OptType::insert && is_insert_skipped(name)) { return; } values.push_back(detail::mysql_async::to_query_arg_impl( diff --git a/ormpp/postgresql.hpp b/ormpp/postgresql.hpp index 90e40c9d..ca030306 100644 --- a/ormpp/postgresql.hpp +++ b/ormpp/postgresql.hpp @@ -574,8 +574,7 @@ class postgresql { else { ylt::reflection::for_each(t, [arr, ¶m_values, type, this]( auto &field, auto name, auto index) { - if (type == OptType::insert && - (is_auto_key(name) || is_skip_insert_field(name))) { + if (type == OptType::insert && is_insert_skipped(name)) { return; } if constexpr (sizeof...(members) > 0) { diff --git a/ormpp/sqlite.hpp b/ormpp/sqlite.hpp index 367246da..1c082042 100644 --- a/ormpp/sqlite.hpp +++ b/ormpp/sqlite.hpp @@ -601,8 +601,7 @@ class sqlite { else { ylt::reflection::for_each(t, [&bind_ok, &index, type, this]( auto &field, auto name, auto /*index*/) { - if ((type == OptType::insert && - (is_auto_key(name) || is_skip_insert_field(name))) || + if ((type == OptType::insert && is_insert_skipped(name)) || !bind_ok) { return; } diff --git a/ormpp/utility.hpp b/ormpp/utility.hpp index 41282b82..4abc7cef 100644 --- a/ormpp/utility.hpp +++ b/ormpp/utility.hpp @@ -89,9 +89,13 @@ inline int add_skip_insert_field(std::string_view key, std::string_view value) { template inline auto is_skip_insert_field(std::string_view field_name) { auto it = get_skip_insert_field_map().find(get_short_struct_name()); - return it == get_skip_insert_field_map().end() - ? false - : it->second.find(field_name) != it->second.end(); + return it != get_skip_insert_field_map().end() && + it->second.find(field_name) != it->second.end(); +} + +template +inline bool is_insert_skipped(std::string_view field_name) { + return is_auto_key(field_name) || is_skip_insert_field(field_name); } inline std::string quote_mysql_identifier(std::string_view name) { @@ -552,8 +556,7 @@ inline std::string generate_insert_sql(DBType db_type, bool insert, size_t selected_count = 0; for (size_t i = 0; i < Count; ++i) { std::string field_name(ylt::reflection::name_of(i)); - if (insert && - (is_auto_key(field_name) || is_skip_insert_field(field_name))) { + if (insert && is_insert_skipped(field_name)) { continue; } if (selected_count++ != 0) { diff --git a/tests/test_ormpp.cpp b/tests/test_ormpp.cpp index 849d631e..7fc6127b 100644 --- a/tests/test_ormpp.cpp +++ b/tests/test_ormpp.cpp @@ -99,8 +99,10 @@ REGISTER_SKIP_INSERT_FIELD(insert_default_field, datetime) struct only_default_field { int id; + std::string datetime; }; REGISTER_AUTO_KEY(only_default_field, id) +REGISTER_SKIP_INSERT_FIELD(only_default_field, datetime) struct fake_postgresql_db { static constexpr DBType db_type_v = DBType::postgresql; @@ -1835,13 +1837,28 @@ TEST_CASE("insert with only database default fields") { REQUIRE(sqlite.connect(db)); #endif sqlite.execute("drop table if exists only_default_field"); - REQUIRE( - sqlite.execute("create table only_default_field(id integer primary " - "key autoincrement)")); - REQUIRE(sqlite.insert(only_default_field{0}) == 1); + REQUIRE(sqlite.execute( + "create table only_default_field(id integer primary key autoincrement, " + "datetime text default 'db_default')")); + REQUIRE(sqlite.insert(only_default_field{0, "ignored"}) == 1); auto rows = sqlite.query_s(); REQUIRE(rows.size() == 1); CHECK(rows.front().id > 0); + CHECK(rows.front().datetime == "db_default"); + +#ifdef ORMPP_ENABLE_MYSQL + dbng mysql; + if (mysql.connect(ip, username, password, db)) { + mysql.execute("drop table if exists only_default_field"); + REQUIRE(mysql.execute( + "create table only_default_field(id int auto_increment primary key, " + "datetime varchar(20) default 'db_default')")); + REQUIRE(mysql.insert(only_default_field{0, "ignored"}) == 1); + auto mysql_rows = mysql.query_s(); + REQUIRE(mysql_rows.size() == 1); + CHECK(mysql_rows.front().datetime == "db_default"); + } +#endif } TEST_CASE("insert query") {