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 {
55public:
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
87private:
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 {
98public:
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 bool operator==(const std::default_sentinel_t& other) const noexcept;
118
119private:
120 friend class Cursor;
121
122 explicit CursorIterator(Cursor& cursor);
123
124 impl::ParseState* parse_state_{nullptr};
125};
126
127class Cursor final {
128public:
129 /// @cond
130 explicit Cursor(const NYdb::TResultSet& result_set);
131 /// @endcond
132
133 Cursor(const Cursor&) = delete;
134 Cursor(Cursor&&) noexcept = default;
135 Cursor& operator=(const Cursor&) = delete;
136 Cursor& operator=(Cursor&&) noexcept = default;
137
138 size_t ColumnsCount() const;
139 size_t RowsCount() const;
140 /// @throws EmptyResponseError if GetFirstRow() or begin() called before or
141 /// cursor is empty
142 Row GetFirstRow();
143
144 /// Returns true if response has been truncated to the database limit
145 /// (currently 1000 rows)
146 bool IsTruncated() const;
147
148 bool empty() const;
149 std::size_t size() const;
150
151 CursorIterator begin();
152 std::default_sentinel_t end();
153
154private:
155 friend class Row;
156 friend class CursorIterator;
157
158 bool truncated_;
159 bool is_consumed_{false};
160 // Row should not be invalidated after moving Cursor, hence the indirection.
161 utils::UniqueRef<impl::ParseState> parse_state_;
162};
163
164class ExecuteResponse final {
165public:
166 /// @cond
167 explicit ExecuteResponse(NYdb::NTable::TDataQueryResult&& query_result);
168 /// @endcond
169
170 ExecuteResponse(const ExecuteResponse&) = delete;
171 ExecuteResponse(ExecuteResponse&&) noexcept = default;
172 ExecuteResponse& operator=(const ExecuteResponse&) = delete;
173 ExecuteResponse& operator=(ExecuteResponse&&) = delete;
174
175 std::size_t GetCursorCount() const;
176 Cursor GetCursor(std::size_t index) const;
177 Cursor GetSingleCursor() const;
178
179 /// Query stats are only available if initially requested
180 const std::optional<NYdb::NTable::TQueryStats>& //
181 GetQueryStats() const noexcept;
182
183 /// Returns true if Execute used the server query cache
184 bool IsFromServerQueryCache() const noexcept;
185
186private:
187 void EnsureResultSetsNotEmpty() const;
188
189 std::optional<NYdb::NTable::TQueryStats> query_stats_;
190 std::vector<NYdb::TResultSet> result_sets_;
191};
192
193class ReadTableResults final {
194public:
195 /// @cond
196 explicit ReadTableResults(NYdb::NTable::TTablePartIterator iterator);
197 /// @endcond
198
199 std::optional<Cursor> GetNextResult();
200
201 ReadTableResults(const ReadTableResults&) = delete;
202 ReadTableResults(ReadTableResults&&) noexcept = default;
203 ReadTableResults& operator=(const ReadTableResults&) = delete;
204 ReadTableResults& operator=(ReadTableResults&&) = delete;
205
206private:
207 NYdb::NTable::TTablePartIterator iterator_;
208};
209
210class ScanQueryResults final {
211 using TScanQueryPartIterator = NYdb::NTable::TScanQueryPartIterator;
212
213public:
214 using TScanQueryPart = NYdb::NTable::TScanQueryPart;
215
216 /// @cond
217 explicit ScanQueryResults(TScanQueryPartIterator iterator);
218 /// @endcond
219
220 std::optional<TScanQueryPart> GetNextResult();
221
222 std::optional<Cursor> GetNextCursor();
223
224 ScanQueryResults(const ScanQueryResults&) = delete;
225 ScanQueryResults(ScanQueryResults&&) noexcept = default;
226 ScanQueryResults& operator=(const ScanQueryResults&) = delete;
227 ScanQueryResults& operator=(ScanQueryResults&&) = delete;
228
229private:
230 TScanQueryPartIterator iterator_;
231};
232
233template <typename T>
234T Row::As() && {
235 if (&typeid(T) != parse_state_.row_type_id) {
236 parse_state_.cpp_to_ydb_field_mapping = impl::StructRowParser<T>::MakeCppToYdbFieldMapping(parse_state_.parser);
237 parse_state_.row_type_id = &typeid(T);
238 }
239 return impl::StructRowParser<T>::ParseRow(parse_state_.parser, parse_state_.cpp_to_ydb_field_mapping);
240}
241
242template <typename T>
243T Row::Get(std::string_view column_name) {
244#ifndef NDEBUG
245 ConsumedColumnsCheck(parse_state_.parser.ColumnIndex(impl::ToString(column_name)));
246#endif
247 auto& column = GetColumn(column_name);
248 return Parse<T>(column, ParseContext{/*column_name=*/column_name});
249}
250
251template <typename T>
252T Row::Get(std::size_t column_index) {
253#ifndef NDEBUG
254 ConsumedColumnsCheck(column_index);
255#endif
256 auto& column = GetColumn(column_index);
257 const auto column_name = std::to_string(column_index);
258 return Parse<T>(column, ParseContext{/*column_name=*/column_name});
259}
260
261} // namespace ydb
262
263USERVER_NAMESPACE_END