userver: userver/ydb/response.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
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