Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,23 @@ 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` 字段,数据库会使用表定义中的默认值。如果某次插入需要显式提供该字段,请使用自定义 SQL;`replace` 和 `update` 不受影响。
请在启动并发数据库操作前完成字段注册;`REGISTER_SKIP_INSERT_FIELD` 与 `REGISTER_AUTO_KEY` 一样,面向静态初始化期使用。

## 冲突主键

使用REGISTER_CONFLICT_KEY注册冲突主键来进行update,如果未注册冲突主键则会采用自增主键
Expand Down Expand Up @@ -532,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+。

### 数据库的安装

Expand Down
20 changes: 13 additions & 7 deletions ormpp/mysql.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1378,13 +1378,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()));
}
}
});
Expand All @@ -1398,7 +1398,9 @@ class mysql {
T t;
ylt::reflection::for_each(t, [&](auto &field, auto name, size_t index) {
using item_type = std::decay_t<decltype(field)>;
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);

Expand All @@ -1417,12 +1419,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("),");
Expand Down Expand Up @@ -1459,7 +1463,8 @@ class mysql {
t, [arr, &param_binds, &text_param_storage, &blob_param_storage,
type, &param_length_storage, &param_null_storage,
this](auto &field, auto name, auto index) {
if (type == OptType::insert && is_auto_key<T>(name)) {
if (type == OptType::insert &&
(is_auto_key<T>(name) || is_skip_insert_field<T>(name))) {
return;
}
if constexpr (sizeof...(members) > 0) {
Expand Down Expand Up @@ -1501,7 +1506,8 @@ class mysql {
return INT_MIN;
}

if (mysql_stmt_bind_param(stmt_, &param_binds[0])) {
if (!param_binds.empty() &&
mysql_stmt_bind_param(stmt_, param_binds.data())) {
set_last_error(mysql_stmt_error(stmt_));
return INT_MIN;
}
Expand Down
17 changes: 11 additions & 6 deletions ormpp/mysql_async.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1015,13 +1015,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()));
}
}
},
Expand All @@ -1037,7 +1037,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() &&
Expand All @@ -1054,12 +1056,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(',');
Expand Down Expand Up @@ -1856,7 +1860,8 @@ class mysql_async {

ylt::reflection::for_each(
value, [&](auto& field, auto name, auto /*index*/) {
if (type == OptType::insert && is_auto_key<T>(name)) {
if (type == OptType::insert &&
(is_auto_key<T>(name) || is_skip_insert_field<T>(name))) {
return;
}
values.push_back(detail::mysql_async::to_query_arg_impl(
Expand Down
18 changes: 9 additions & 9 deletions ormpp/postgresql.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,8 +553,9 @@ class postgresql {
#ifdef ORMPP_ENABLE_LOG
std::cout << sql << std::endl;
#endif
res_ = PQprepare(con_, "", sql.data(), ylt::reflection::members_count_v<T>,
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;
}
Expand All @@ -573,7 +574,8 @@ class postgresql {
else {
ylt::reflection::for_each(t, [arr, &param_values, type, this](
auto &field, auto name, auto index) {
if (type == OptType::insert && is_auto_key<T>(name)) {
if (type == OptType::insert &&
(is_auto_key<T>(name) || is_skip_insert_field<T>(name))) {
return;
}
if constexpr (sizeof...(members) > 0) {
Expand All @@ -600,10 +602,6 @@ class postgresql {
}
}

if (param_values.empty()) {
return std::nullopt;
}

auto param_values_buf = make_param_value_buffers(param_values);

res_ = PQexecPrepared(con_, "", (int)param_values.size(),
Expand Down Expand Up @@ -675,7 +673,8 @@ class postgresql {
bool get_insert_id = false,
Args &&...args) {
if (!prepare<T>(get_insert_id
? sql + "returning " + get_auto_key<T>().data()
? sql + "returning " +
quote_postgresql_identifier(get_auto_key<T>())
: sql)) {
return std::nullopt;
}
Expand All @@ -694,7 +693,8 @@ class postgresql {
}

if (!prepare<T>(get_insert_id
? sql + "returning " + get_auto_key<T>().data()
? sql + "returning " +
quote_postgresql_identifier(get_auto_key<T>())
: sql)) {
return std::nullopt;
}
Expand Down
4 changes: 3 additions & 1 deletion ormpp/sqlite.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,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<T>(name)) || !bind_ok) {
if ((type == OptType::insert &&
(is_auto_key<T>(name) || is_skip_insert_field<T>(name))) ||
!bind_ok) {
return;
}
bind_ok = set_param_bind(field, ++index);
Expand Down
106 changes: 86 additions & 20 deletions ormpp/utility.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <mutex>
#include <optional>
#include <sstream>
#include <unordered_map>
#include <unordered_set>

#include "entity.hpp"
#include "iguana/util.hpp"
Expand Down Expand Up @@ -72,6 +74,64 @@ 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<std::string_view,
std::unordered_set<std::string_view>>
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 <typename T>
inline auto is_skip_insert_field(std::string_view field_name) {
auto it = get_skip_insert_field_map().find(get_short_struct_name<T>());
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) {
bool quoted = name.size() >= 2 && name.front() == '`' && name.back() == '`';
if (quoted) {
name.remove_prefix(1);
name.remove_suffix(1);
}

std::string result;
result.reserve(name.size() + 2);
result.push_back('`');
for (size_t i = 0; i < name.size(); ++i) {
char ch = name[i];
if (ch == '`') {
result.push_back('`');
if (quoted && i + 1 < name.size() && name[i + 1] == '`') {
++i;
}
}
result.push_back(ch);
}
result.push_back('`');
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
Expand All @@ -82,6 +142,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<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<STRUCT_NAME>(), #FIELD);

inline auto &get_conflict_map() {
static std::unordered_map<std::string_view, std::string_view> map;
return map;
Expand Down Expand Up @@ -321,7 +386,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<T>()) {
if (db_type == DBType::mysql) {
fields += "`" + std::string(it) + "`";
fields += quote_mysql_identifier(it);
}
else {
fields += std::string(it);
Expand Down Expand Up @@ -414,7 +479,7 @@ inline std::vector<std::string> 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);
Expand Down Expand Up @@ -484,38 +549,36 @@ inline std::string generate_insert_sql(DBType db_type, bool insert,
int index = 0;
std::string fields = "(";
std::string values = "values(";
size_t selected_count = 0;
for (size_t i = 0; i < Count; ++i) {
std::string field_name(ylt::reflection::name_of<T>(i));
if (insert && is_auto_key<T>(field_name)) {
if (insert &&
(is_auto_key<T>(field_name) || is_skip_insert_field<T>(field_name))) {
continue;
}
if (selected_count++ != 0) {
fields += ",";
values += ",";
}
if (db_type == DBType::postgresql) {
values += "$" + std::to_string(++index);
}
else {
values += "?";
}
if (db_type == DBType::mysql) {
fields += "`" + field_name + "`";
fields += quote_mysql_identifier(field_name);
}
else {
fields += field_name;
}
if (i < Count - 1) {
fields += ",";
values += ",";
}
else {
fields += ")";
values += ")";
}
}
if (fields.back() != ')') {
fields.back() = ')';
}
if (values.back() != ')') {
values.back() = ')';
if (selected_count == 0 && db_type != DBType::mysql) {
append(sql, "default values");
return sql;
}
fields += ")";
values += ")";
append(sql, fields, values);
return sql;
}
Expand All @@ -537,8 +600,11 @@ inline std::string generate_update_sql(DBType db_type, Args &&...args) {
}
else {
(fields
.append(ylt::reflection::name_of<T>(
ylt::reflection::index_of<members>()))
.append(db_type == DBType::mysql
? quote_mysql_identifier(ylt::reflection::name_of<T>(
ylt::reflection::index_of<members>()))
: std::string(ylt::reflection::name_of<T>(
ylt::reflection::index_of<members>())))
.append("=?,"),
...);
}
Expand All @@ -548,7 +614,7 @@ inline std::string generate_update_sql(DBType db_type, Args &&...args) {
for (size_t i = 0; i < Count; ++i) {
std::string field_name(ylt::reflection::name_of<T>(i));
if (db_type == DBType::mysql) {
fields.append("`").append(field_name).append("`");
fields.append(quote_mysql_identifier(field_name));
}
else {
fields.append(field_name);
Expand Down
Loading
Loading