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