5#include <userver/utils/meta.hpp>
7#include <userver/storages/mysql/impl/io/result_binder.hpp>
9#include <userver/storages/mysql/convert.hpp>
11USERVER_NAMESPACE_BEGIN
15template <
typename Container>
16class InPlaceStorage
final {
18 explicit InPlaceStorage(ResultBinder& binder);
20 void Reserve(std::size_t size);
22 template <
typename ExtractionTag>
23 impl::bindings::OutputBindings& BindNextRow();
27 void RollbackLastRow();
29 Container&& ExtractData();
33 ResultBinder& binder_;
36template <
typename Container,
typename MapFrom>
37class ProxyingStorage
final {
39 explicit ProxyingStorage(ResultBinder& binder);
41 void Reserve(std::size_t size);
43 template <
typename ExtractionTag>
44 impl::bindings::OutputBindings& BindNextRow();
48 void RollbackLastRow();
50 Container&& ExtractData();
53 static constexpr bool kIsMapped =
54 !std::is_same_v<
typename Container::value_type, MapFrom>;
58 ResultBinder& binder_;
63 ExtractorBase(std::size_t size);
65 virtual void Reserve(std::size_t size) = 0;
67 virtual OutputBindingsFwd& BindNextRow() = 0;
68 virtual void CommitLastRow() = 0;
69 virtual void RollbackLastRow() = 0;
71 virtual std::size_t ColumnsCount()
const = 0;
73 void UpdateBinds(
void* binds_array);
81template <
typename Container,
typename MapFrom,
typename ExtractionTag>
82class TypedExtractor
final :
public ExtractorBase {
86 void Reserve(std::size_t size)
final;
88 impl::bindings::OutputBindings& BindNextRow()
final;
90 void CommitLastRow()
final;
92 void RollbackLastRow()
final;
94 std::size_t ColumnsCount()
const final;
96 Container&& ExtractData();
99 static constexpr std::size_t GetColumnsCount() {
100 if constexpr (std::is_same_v<ExtractionTag,
RowTag>) {
101 return boost::pfr::tuple_size_v<MapFrom>;
102 }
else if constexpr (std::is_same_v<ExtractionTag,
FieldTag>) {
105 static_assert(!
sizeof(ExtractionTag),
"should be unreachable");
109 static constexpr bool kIsMapped =
110 !std::is_same_v<
typename Container::value_type, MapFrom>;
113 static constexpr bool kCanBindInPlace =
114 std::is_same_v<Container, std::vector<MapFrom>>;
120 using InternalStorageType =
121 std::conditional_t<!kIsMapped && kCanBindInPlace,
122 InPlaceStorage<Container>,
123 ProxyingStorage<Container, MapFrom>>;
124 InternalStorageType storage_;
127template <
typename Container,
typename MapFrom,
typename ExtractionTag>
128TypedExtractor<Container, MapFrom, ExtractionTag>::TypedExtractor()
129 : ExtractorBase{GetColumnsCount()},
130 storage_{InternalStorageType{binder_}} {}
132template <
typename Container,
typename MapFrom,
typename ExtractionTag>
133void TypedExtractor<Container, MapFrom, ExtractionTag>::Reserve(
135 storage_.Reserve(size);
138template <
typename Container,
typename MapFrom,
typename ExtractionTag>
139impl::bindings::OutputBindings&
140TypedExtractor<Container, MapFrom, ExtractionTag>::BindNextRow() {
141 return storage_.
template BindNextRow<ExtractionTag>();
144template <
typename Container,
typename MapFrom,
typename ExtractionTag>
145void TypedExtractor<Container, MapFrom, ExtractionTag>::CommitLastRow() {
146 storage_.CommitLastRow();
149template <
typename Container,
typename MapFrom,
typename ExtractionTag>
150void TypedExtractor<Container, MapFrom, ExtractionTag>::RollbackLastRow() {
151 storage_.RollbackLastRow();
154template <
typename Container,
typename MapFrom,
typename ExtractionTag>
155std::size_t TypedExtractor<Container, MapFrom, ExtractionTag>::ColumnsCount()
157 return GetColumnsCount();
160template <
typename Container,
typename MapFrom,
typename ExtractionTag>
161Container&& TypedExtractor<Container, MapFrom, ExtractionTag>::ExtractData() {
162 return storage_.ExtractData();
165template <
typename Container>
166InPlaceStorage<Container>::InPlaceStorage(ResultBinder& binder)
169template <
typename Container>
170void InPlaceStorage<Container>::Reserve(std::size_t size) {
173 data_.reserve(size + 1);
176template <
typename Container>
177template <
typename ExtractionTag>
178impl::bindings::OutputBindings& InPlaceStorage<Container>::BindNextRow() {
179 data_.emplace_back();
180 return binder_.BindTo(data_.back(), ExtractionTag{});
183template <
typename Container>
184void InPlaceStorage<Container>::CommitLastRow() {
188template <
typename Container>
189void InPlaceStorage<Container>::RollbackLastRow() {
193template <
typename Container>
194Container&& InPlaceStorage<Container>::ExtractData() {
195 return std::move(data_);
198template <
typename Container,
typename MapFrom>
199ProxyingStorage<Container, MapFrom>::ProxyingStorage(ResultBinder& binder)
202template <
typename Container,
typename MapFrom>
203void ProxyingStorage<Container, MapFrom>::Reserve(
204 [[maybe_unused]] std::size_t size) {
205 if constexpr (meta::kIsReservable<Container>) {
210template <
typename Container,
typename MapFrom>
211template <
typename ExtractionTag>
212impl::bindings::OutputBindings&
213ProxyingStorage<Container, MapFrom>::BindNextRow() {
214 return binder_.BindTo(row_, ExtractionTag{});
217template <
typename Container,
typename MapFrom>
218void ProxyingStorage<Container, MapFrom>::CommitLastRow() {
219 if constexpr (kIsMapped) {
220 *meta::Inserter(data_) =
224 *meta::Inserter(data_) = std::move(row_);
228template <
typename Container,
typename MapFrom>
229void ProxyingStorage<Container, MapFrom>::RollbackLastRow() {
233template <
typename Container,
typename MapFrom>
234Container&& ProxyingStorage<Container, MapFrom>::ExtractData() {
235 return std::move(data_);