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
 
   16namespace storages::
mysql::impl::io {
 
   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) {
 
   81          storages::
mysql::
convert::DoConvert<Row>(*current_row_it_);
 
   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_;