userver: userver/storages/mysql/impl/io/extractor.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
extractor.hpp
1#pragma once
2
3#include <vector>
4
5#include <userver/utils/meta.hpp>
6
7#include <userver/storages/mysql/impl/io/result_binder.hpp>
8
9#include <userver/storages/mysql/convert.hpp>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace storages::mysql::impl::io {
14
15template <typename Container>
16class InPlaceStorage final {
17 public:
18 explicit InPlaceStorage(ResultBinder& binder);
19
20 void Reserve(std::size_t size);
21
22 template <typename ExtractionTag>
23 impl::bindings::OutputBindings& BindNextRow();
24
25 void CommitLastRow();
26
27 void RollbackLastRow();
28
29 Container&& ExtractData();
30
31 private:
32 Container data_;
33 ResultBinder& binder_;
34};
35
36template <typename Container, typename MapFrom>
37class ProxyingStorage final {
38 public:
39 explicit ProxyingStorage(ResultBinder& binder);
40
41 void Reserve(std::size_t size);
42
43 template <typename ExtractionTag>
44 impl::bindings::OutputBindings& BindNextRow();
45
46 void CommitLastRow();
47
48 void RollbackLastRow();
49
50 Container&& ExtractData();
51
52 private:
53 static constexpr bool kIsMapped =
54 !std::is_same_v<typename Container::value_type, MapFrom>;
55
56 MapFrom row_;
57 Container data_;
58 ResultBinder& binder_;
59};
60
61class ExtractorBase {
62 public:
63 ExtractorBase(std::size_t size);
64
65 virtual void Reserve(std::size_t size) = 0;
66
67 virtual OutputBindingsFwd& BindNextRow() = 0;
68 virtual void CommitLastRow() = 0;
69 virtual void RollbackLastRow() = 0;
70
71 virtual std::size_t ColumnsCount() const = 0;
72
73 void UpdateBinds(void* binds_array);
74
75 protected:
76 ~ExtractorBase();
77 // NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
78 ResultBinder binder_;
79};
80
81template <typename Container, typename MapFrom, typename ExtractionTag>
82class TypedExtractor final : public ExtractorBase {
83 public:
84 TypedExtractor();
85
86 void Reserve(std::size_t size) final;
87
88 impl::bindings::OutputBindings& BindNextRow() final;
89
90 void CommitLastRow() final;
91
92 void RollbackLastRow() final;
93
94 std::size_t ColumnsCount() const final;
95
96 Container&& ExtractData();
97
98 private:
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>) {
103 return 1;
104 } else {
105 static_assert(!sizeof(ExtractionTag), "should be unreachable");
106 }
107 }
108
109 static constexpr bool kIsMapped =
110 !std::is_same_v<typename Container::value_type, MapFrom>;
111 // TODO : add more std:: containers that allow us to emplace/emplace_back and
112 // then update the value (list, queue, deque etc)
113 static constexpr bool kCanBindInPlace =
114 std::is_same_v<Container, std::vector<MapFrom>>;
115
116 // If we are mapped we are out of luck - have to create a MapFrom instance
117 // first, and then insert it.
118 // If we can't bind in place the same applies.
119 // Otherwise, we can emplace_back and bind that new instance.
120 using InternalStorageType =
121 std::conditional_t<!kIsMapped && kCanBindInPlace,
122 InPlaceStorage<Container>,
123 ProxyingStorage<Container, MapFrom>>;
124 InternalStorageType storage_;
125};
126
127template <typename Container, typename MapFrom, typename ExtractionTag>
128TypedExtractor<Container, MapFrom, ExtractionTag>::TypedExtractor()
129 : ExtractorBase{GetColumnsCount()},
130 storage_{InternalStorageType{binder_}} {}
131
132template <typename Container, typename MapFrom, typename ExtractionTag>
133void TypedExtractor<Container, MapFrom, ExtractionTag>::Reserve(
134 std::size_t size) {
135 storage_.Reserve(size);
136}
137
138template <typename Container, typename MapFrom, typename ExtractionTag>
139impl::bindings::OutputBindings&
140TypedExtractor<Container, MapFrom, ExtractionTag>::BindNextRow() {
141 return storage_.template BindNextRow<ExtractionTag>();
142}
143
144template <typename Container, typename MapFrom, typename ExtractionTag>
145void TypedExtractor<Container, MapFrom, ExtractionTag>::CommitLastRow() {
146 storage_.CommitLastRow();
147}
148
149template <typename Container, typename MapFrom, typename ExtractionTag>
150void TypedExtractor<Container, MapFrom, ExtractionTag>::RollbackLastRow() {
151 storage_.RollbackLastRow();
152}
153
154template <typename Container, typename MapFrom, typename ExtractionTag>
155std::size_t TypedExtractor<Container, MapFrom, ExtractionTag>::ColumnsCount()
156 const {
157 return GetColumnsCount();
158}
159
160template <typename Container, typename MapFrom, typename ExtractionTag>
161Container&& TypedExtractor<Container, MapFrom, ExtractionTag>::ExtractData() {
162 return storage_.ExtractData();
163}
164
165template <typename Container>
166InPlaceStorage<Container>::InPlaceStorage(ResultBinder& binder)
167 : binder_{binder} {}
168
169template <typename Container>
170void InPlaceStorage<Container>::Reserve(std::size_t size) {
171 // +1 because we over-commit one row and then rollback it in default execution
172 // path
173 data_.reserve(size + 1);
174}
175
176template <typename Container>
177template <typename ExtractionTag>
178impl::bindings::OutputBindings& InPlaceStorage<Container>::BindNextRow() {
179 data_.emplace_back();
180 return binder_.BindTo(data_.back(), ExtractionTag{});
181}
182
183template <typename Container>
184void InPlaceStorage<Container>::CommitLastRow() {
185 // no-op, already committed
186}
187
188template <typename Container>
189void InPlaceStorage<Container>::RollbackLastRow() {
190 data_.pop_back();
191}
192
193template <typename Container>
194Container&& InPlaceStorage<Container>::ExtractData() {
195 return std::move(data_);
196}
197
198template <typename Container, typename MapFrom>
199ProxyingStorage<Container, MapFrom>::ProxyingStorage(ResultBinder& binder)
200 : binder_{binder} {}
201
202template <typename Container, typename MapFrom>
203void ProxyingStorage<Container, MapFrom>::Reserve(std::size_t size) {
204 if constexpr (meta::kIsReservable<Container>) {
205 data_.reserve(size);
206 }
207}
208
209template <typename Container, typename MapFrom>
210template <typename ExtractionTag>
211impl::bindings::OutputBindings&
212ProxyingStorage<Container, MapFrom>::BindNextRow() {
213 return binder_.BindTo(row_, ExtractionTag{});
214}
215
216template <typename Container, typename MapFrom>
217void ProxyingStorage<Container, MapFrom>::CommitLastRow() {
218 if constexpr (kIsMapped) {
219 *meta::Inserter(data_) =
220 storages::mysql::convert::DoConvert<typename Container::value_type>(
221 std::move(row_));
222 } else {
223 *meta::Inserter(data_) = std::move(row_);
224 }
225}
226
227template <typename Container, typename MapFrom>
228void ProxyingStorage<Container, MapFrom>::RollbackLastRow() {
229 // no-op, because either this function or commit is called, not both
230}
231
232template <typename Container, typename MapFrom>
233Container&& ProxyingStorage<Container, MapFrom>::ExtractData() {
234 return std::move(data_);
235}
236
237} // namespace storages::mysql::impl::io
238
239USERVER_NAMESPACE_END