userver: userver/ydb/response.hpp Source File
Loading...
Searching...
No Matches
response.hpp
1#pragma once
2
3#include <ydb-cpp-sdk/client/result/result.h>
4#include <ydb-cpp-sdk/client/table/table.h>
5
6#include <cstddef>
7#include <iterator>
8#include <memory>
9#include <optional>
10#include <typeinfo>
11
12#include <userver/utils/not_null.hpp>
13#include <userver/ydb/impl/cast.hpp>
14#include <userver/ydb/io/insert_row.hpp>
15#include <userver/ydb/io/list.hpp>
16#include <userver/ydb/io/primitives.hpp>
17#include <userver/ydb/io/traits.hpp>
18#include <userver/ydb/types.hpp>
19
20namespace NYdb {
21class TResultSetParser;
22class TResultSet;
23class TValueParser;
24
25namespace NTable {
26class TDataQueryResult;
27class TTablePartIterator;
28} // namespace NTable
29} // namespace NYdb
30
31USERVER_NAMESPACE_BEGIN
32
33namespace ydb {
34
35namespace impl {
36
37template <typename T>
38struct StructRowParser;
39
40struct ParseState final {
41 explicit ParseState(const NYdb::TResultSet& result_set);
42
43 NYdb::TResultSetParser parser;
44 const std::type_info* row_type_id{nullptr};
45 std::unique_ptr<std::size_t[]> cpp_to_ydb_field_mapping{};
46};
47
48} // namespace impl
49
50using ValueType = NYdb::EPrimitiveType;
51
52class Cursor;
53
54class Row final {
55 public:
56 /// @cond
57 // For internal use only.
58 explicit Row(impl::ParseState& parse_state);
59 /// @endcond
60
61 Row(const Row&) = delete;
62 Row(Row&&) noexcept = default;
63 Row& operator=(const Row&) = delete;
64 Row& operator=(Row&&) = delete;
65
66 /// @brief Parses the whole row to `T`, which must be a struct type.
67 /// ydb::kStructMemberNames must be specialized for `T`.
68 ///
69 /// @throws ydb::ColumnParseError on parsing error
70 /// @throws ydb::ParseError on extra fields on C++ side
71 /// @throws ydb::ParseError on extra fields on YDB side
72 template <typename T>
73 T As() &&;
74
75 /// @brief Parses the specified column to `T`.
76 /// `Get` can only be called once for each column.
77 /// @throws ydb::BaseError on parsing error
78 template <typename T>
79 T Get(std::string_view column_name);
80
81 /// @brief Parses the specified column to `T`.
82 /// `Get` can only be called once for each column.
83 /// @throws ydb::BaseError on parsing error
84 template <typename T>
85 T Get(std::size_t column_index);
86
87 private:
88 NYdb::TValueParser& GetColumn(std::size_t index);
89 NYdb::TValueParser& GetColumn(std::string_view name);
90
91 void ConsumedColumnsCheck(std::size_t column_index);
92
93 impl::ParseState& parse_state_;
94 std::vector<bool> consumed_columns_;
95};
96
97class CursorIterator final {
98 public:
99 using difference_type = std::ptrdiff_t;
100 using value_type = Row;
101 using reference = Row;
102 using iterator_category = std::input_iterator_tag;
103
104 CursorIterator() = default;
105
106 CursorIterator(const CursorIterator&) = delete;
107 CursorIterator(CursorIterator&&) noexcept = default;
108 CursorIterator& operator=(const CursorIterator&) = delete;
109 CursorIterator& operator=(CursorIterator&&) = default;
110
111 Row operator*() const;
112
113 CursorIterator& operator++();
114
115 void operator++(int);
116
117#if __cpp_lib_ranges >= 201911L
118 bool operator==(const std::default_sentinel_t& other) const noexcept;
119#else
120 bool operator==(const CursorIterator& other) const noexcept;
121#endif
122
123 private:
124 friend class Cursor;
125
126 explicit CursorIterator(Cursor& cursor);
127
128 impl::ParseState* parse_state_{nullptr};
129};
130
131class Cursor final {
132 public:
133 /// @cond
134 explicit Cursor(const NYdb::TResultSet& result_set);
135 /// @endcond
136
137 Cursor(const Cursor&) = delete;
138 Cursor(Cursor&&) noexcept = default;
139 Cursor& operator=(const Cursor&) = delete;
140 Cursor& operator=(Cursor&&) noexcept = default;
141
142 size_t ColumnsCount() const;
143 size_t RowsCount() const;
144 /// @throws EmptyResponseError if GetFirstRow() or begin() called before or
145 /// cursor is empty
146 Row GetFirstRow();
147
148 /// Returns true if response has been truncated to the database limit
149 /// (currently 1000 rows)
150 bool IsTruncated() const;
151
152 bool empty() const;
153 std::size_t size() const;
154
155 CursorIterator begin();
156
157#if __cpp_lib_ranges >= 201911L
158 std::default_sentinel_t end();
159#else
160 CursorIterator end();
161#endif
162
163 private:
164 friend class Row;
165 friend class CursorIterator;
166
167 bool truncated_;
168 bool is_consumed_{false};
169 // Row should not be invalidated after moving Cursor, hence the indirection.
170 utils::UniqueRef<impl::ParseState> parse_state_;
171};
172
173class ExecuteResponse final {
174 public:
175 /// @cond
176 explicit ExecuteResponse(NYdb::NTable::TDataQueryResult&& query_result);
177 /// @endcond
178
179 ExecuteResponse(const ExecuteResponse&) = delete;
180 ExecuteResponse(ExecuteResponse&&) noexcept = default;
181 ExecuteResponse& operator=(const ExecuteResponse&) = delete;
182 ExecuteResponse& operator=(ExecuteResponse&&) = delete;
183
184 std::size_t GetCursorCount() const;
185 Cursor GetCursor(std::size_t index) const;
186 Cursor GetSingleCursor() const;
187
188 /// Query stats are only available if initially requested
189 const std::optional<NYdb::NTable::TQueryStats>& //
190 GetQueryStats() const noexcept;
191
192 /// Returns true if Execute used the server query cache
193 bool IsFromServerQueryCache() const noexcept;
194
195 private:
196 void EnsureResultSetsNotEmpty() const;
197
198 std::optional<NYdb::NTable::TQueryStats> query_stats_;
199 std::vector<NYdb::TResultSet> result_sets_;
200};
201
202class ReadTableResults final {
203 public:
204 /// @cond
205 explicit ReadTableResults(NYdb::NTable::TTablePartIterator iterator);
206 /// @endcond
207
208 std::optional<Cursor> GetNextResult();
209
210 ReadTableResults(const ReadTableResults&) = delete;
211 ReadTableResults(ReadTableResults&&) noexcept = default;
212 ReadTableResults& operator=(const ReadTableResults&) = delete;
213 ReadTableResults& operator=(ReadTableResults&&) = delete;
214
215 private:
216 NYdb::NTable::TTablePartIterator iterator_;
217};
218
219class ScanQueryResults final {
220 using TScanQueryPartIterator = NYdb::NTable::TScanQueryPartIterator;
221
222 public:
223 using TScanQueryPart = NYdb::NTable::TScanQueryPart;
224
225 /// @cond
226 explicit ScanQueryResults(TScanQueryPartIterator iterator);
227 /// @endcond
228
229 std::optional<TScanQueryPart> GetNextResult();
230
231 std::optional<Cursor> GetNextCursor();
232
233 ScanQueryResults(const ScanQueryResults&) = delete;
234 ScanQueryResults(ScanQueryResults&&) noexcept = default;
235 ScanQueryResults& operator=(const ScanQueryResults&) = delete;
236 ScanQueryResults& operator=(ScanQueryResults&&) = delete;
237
238 private:
239 TScanQueryPartIterator iterator_;
240};
241
242template <typename T>
243T Row::As() && {
244 if (&typeid(T) != parse_state_.row_type_id) {
245 parse_state_.cpp_to_ydb_field_mapping =
246 impl::StructRowParser<T>::MakeCppToYdbFieldMapping(parse_state_.parser);
247 parse_state_.row_type_id = &typeid(T);
248 }
249 return impl::StructRowParser<T>::ParseRow(
250 parse_state_.parser, parse_state_.cpp_to_ydb_field_mapping);
251}
252
253template <typename T>
254T Row::Get(std::string_view column_name) {
255#ifndef NDEBUG
256 ConsumedColumnsCheck(
257 parse_state_.parser.ColumnIndex(impl::ToString(column_name)));
258#endif
259 auto& column = GetColumn(column_name);
260 return Parse<T>(column, ParseContext{/*column_name=*/column_name});
261}
262
263template <typename T>
264T Row::Get(std::size_t column_index) {
265#ifndef NDEBUG
266 ConsumedColumnsCheck(column_index);
267#endif
268 auto& column = GetColumn(column_index);
269 const auto column_name = std::to_string(column_index);
270 return Parse<T>(column, ParseContext{/*column_name=*/column_name});
271}
272
273} // namespace ydb
274
275USERVER_NAMESPACE_END