Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 字段,数据库会使用表定义中的默认值。如果某次插入需要显式提供该字段,请使用自定义 SQL;`replace` 和 `update` 不受影响。

## 冲突主键

使用REGISTER_CONFLICT_KEY注册冲突主键来进行update,如果未注册冲突主键则会采用自增主键
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
33 changes: 19 additions & 14 deletions ormpp/postgresql.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -549,12 +549,12 @@ class postgresql {
}

template <typename T>
bool prepare(const std::string &sql) {
bool prepare(const std::string &sql,
int parameter_count = ylt::reflection::members_count_v<T>) {
#ifdef ORMPP_ENABLE_LOG
std::cout << sql << std::endl;
#endif
res_ = PQprepare(con_, "", sql.data(), ylt::reflection::members_count_v<T>,
nullptr);
res_ = PQprepare(con_, "", sql.data(), parameter_count, nullptr);
auto guard = guard_statment(res_);
return PQresultStatus(res_) == PGRES_COMMAND_OK;
}
Expand All @@ -573,7 +573,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 +601,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 @@ -674,9 +671,13 @@ class postgresql {
OptType type,
bool get_insert_id = false,
Args &&...args) {
if (!prepare<T>(get_insert_id
? sql + "returning " + get_auto_key<T>().data()
: sql)) {
const int parameter_count =
type == OptType::insert && sql.ends_with("default values ")
? 0
: ylt::reflection::members_count_v<T>;
if (!prepare<T>(
get_insert_id ? sql + "returning " + get_auto_key<T>().data() : sql,
parameter_count)) {
return std::nullopt;
}

Expand All @@ -693,9 +694,13 @@ class postgresql {
return std::nullopt;
}

if (!prepare<T>(get_insert_id
? sql + "returning " + get_auto_key<T>().data()
: sql)) {
const int parameter_count =
type == OptType::insert && sql.ends_with("default values ")
? 0
: ylt::reflection::members_count_v<T>;
if (!prepare<T>(
get_insert_id ? sql + "returning " + get_auto_key<T>().data() : sql,
parameter_count)) {
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
92 changes: 72 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,50 @@ 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;
}

#ifdef _MSC_VER
#define ORMPP_UNIQUE_VARIABLE(str) YLT_CONCAT(str, __COUNTER__)
#else
Expand All @@ -82,6 +128,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 +372,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 +465,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 +535,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 +586,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 +600,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