5#include <boost/pfr/core.hpp>
7#include <userver/utils/assert.hpp>
8#include <userver/utils/meta.hpp>
10#include <userver/storages/mysql/convert.hpp>
11#include <userver/storages/mysql/impl/io/binder_declarations.hpp>
12#include <userver/storages/mysql/impl/io/params_binder_base.hpp>
14USERVER_NAMESPACE_BEGIN
19template <
typename Row,
bool IsMapped>
20struct OwnedMappedRow
final {};
21template <
typename Row>
22struct OwnedMappedRow<Row,
true>
final {
26class InsertBinderBase :
public ParamsBinderBase {
28 explicit InsertBinderBase(std::size_t size);
31 InsertBinderBase(
const InsertBinderBase& other) =
delete;
34 void SetBindCallback(
void* user_data,
35 char (*param_cb)(
void*,
void*, std::size_t));
37 void UpdateBinds(
void* binds_array);
40template <
typename Container,
typename MapTo =
typename Container::value_type>
41class InsertBinder
final :
public InsertBinderBase {
43 explicit InsertBinder(
const Container& container)
44 : InsertBinderBase{kColumnsCount},
45 container_{container},
46 current_row_it_{container_.begin()} {
47 static_assert(kColumnsCount != 0,
"Rows to insert have zero columns");
48 static_assert(meta::kIsSizable<Container>,
49 "Container should be sizeable for batch insertion");
55 SetBindCallback(
this, &BindsRowCallback);
58 std::size_t GetRowsCount()
const final {
return container_.size(); }
62 static constexpr std::size_t kColumnsCount = boost::pfr::tuple_size_v<Row>;
63 static constexpr bool kIsMapped =
64 !std::is_same_v<
typename Container::value_type, Row>;
66 static char BindsRowCallback(
void* user_data,
void* binds_array,
67 std::size_t row_number);
70 UpdateCurrentRowPtr();
71 boost::pfr::for_each_field(
73 [&binds = GetBinds()](
const auto& field, std::size_t i) {
74 storages::mysql::impl::io::BindInput(binds, i, field);
78 void UpdateCurrentRowPtr() {
79 if constexpr (kIsMapped) {
82 current_row_ptr_ = ¤t_row_.value;
84 current_row_ptr_ = &*current_row_it_;
88 void UpdateCurrentRow(std::size_t row_number) {
89 UASSERT(CheckRowNumber(row_number));
90 UASSERT(current_row_it_ != container_.end());
92 if (row_number == 0) {
99 bool CheckRowNumber(std::size_t row_number) {
100 if (row_number < max_row_number_seen_ ||
101 row_number > max_row_number_seen_ + 1) {
105 max_row_number_seen_ = std::max(row_number, max_row_number_seen_);
109 const Container& container_;
111 typename Container::const_iterator current_row_it_;
113 OwnedMappedRow<Row, kIsMapped> current_row_;
114 const Row* current_row_ptr_;
116 std::size_t max_row_number_seen_{0};
119template <
typename Container,
typename MapTo>
120char InsertBinder<Container, MapTo>::BindsRowCallback(
void* user_data,
122 std::size_t row_number) {
123 auto* self =
static_cast<InsertBinder*>(user_data);
126 self->UpdateBinds(binds_array);
127 self->UpdateCurrentRow(row_number);
129 ++self->current_row_it_;