Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
Result.hpp
1#pragma once
2
3#include "data/cassandra/impl/ManagedObject.hpp"
4#include "data/cassandra/impl/Tuple.hpp"
5#include "util/UnsupportedType.hpp"
6
7#include <boost/uuid/string_generator.hpp>
8#include <boost/uuid/uuid.hpp>
9#include <cassandra.h>
10#include <xrpl/basics/base_uint.h>
11#include <xrpl/protocol/AccountID.h>
12
13#include <cstddef>
14#include <cstdint>
15#include <functional>
16#include <iterator>
17#include <optional>
18#include <stdexcept>
19#include <string>
20#include <string_view>
21#include <tuple>
22#include <type_traits>
23#include <utility>
24#include <vector>
25
26namespace data::cassandra::impl {
27
28template <typename Type>
29inline Type
30extractColumn(CassRow const* row, std::size_t idx)
31{
32 using std::to_string;
33 Type output{};
34
35 auto throwErrorIfNeeded = [](CassError rc, std::string_view label) {
36 if (rc != CASS_OK) {
37 auto const tag = '[' + std::string{label} + ']';
38 throw std::logic_error(tag + ": " + cass_error_desc(rc));
39 }
40 };
41
42 using DecayedType = std::decay_t<Type>;
43 using UintTupleType = std::tuple<uint32_t, uint32_t>;
44 using UCharVectorType = std::vector<unsigned char>;
45
46 if constexpr (std::is_same_v<DecayedType, ripple::uint256>) {
47 cass_byte_t const* buf = nullptr;
48 std::size_t bufSize = 0;
49 auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
50 throwErrorIfNeeded(rc, "Extract ripple::uint256");
51 output = ripple::uint256::fromVoid(buf);
52 } else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
53 cass_byte_t const* buf = nullptr;
54 std::size_t bufSize = 0;
55 auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
56 throwErrorIfNeeded(rc, "Extract ripple::AccountID");
57 output = ripple::AccountID::fromVoid(buf);
58 } else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
59 cass_byte_t const* buf = nullptr;
60 std::size_t bufSize = 0;
61 auto const rc = cass_value_get_bytes(cass_row_get_column(row, idx), &buf, &bufSize);
62 throwErrorIfNeeded(rc, "Extract vector<unsigned char>");
63 output = UCharVectorType{buf, buf + bufSize};
64 } else if constexpr (std::is_same_v<DecayedType, UintTupleType>) {
65 auto const* tuple = cass_row_get_column(row, idx);
66 output = TupleIterator::fromTuple(tuple).extract<uint32_t, uint32_t>();
67 } else if constexpr (std::is_convertible_v<DecayedType, std::string>) {
68 char const* value = nullptr;
69 std::size_t len = 0;
70 auto const rc = cass_value_get_string(cass_row_get_column(row, idx), &value, &len);
71 throwErrorIfNeeded(rc, "Extract string");
72 output = std::string{value, len};
73 } else if constexpr (std::is_same_v<DecayedType, bool>) {
74 cass_bool_t flag = cass_bool_t::cass_false;
75 auto const rc = cass_value_get_bool(cass_row_get_column(row, idx), &flag);
76 throwErrorIfNeeded(rc, "Extract bool");
77 output = flag != cass_bool_t::cass_false;
78 }
79 // clio only uses bigint (int64_t) so we convert any incoming type
80 else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
81 int64_t out = 0;
82 auto const rc = cass_value_get_int64(cass_row_get_column(row, idx), &out);
83 throwErrorIfNeeded(rc, "Extract int64");
84 output = static_cast<DecayedType>(out);
85 } else if constexpr (std::is_convertible_v<DecayedType, boost::uuids::uuid>) {
86 CassUuid uuid;
87 auto const rc = cass_value_get_uuid(cass_row_get_column(row, idx), &uuid);
88 throwErrorIfNeeded(rc, "Extract uuid");
89 std::string uuidStr(CASS_UUID_STRING_LENGTH, '0');
90 cass_uuid_string(uuid, uuidStr.data());
91 uuidStr.pop_back(); // remove the last \0 character
92 output = boost::uuids::string_generator{}(uuidStr);
93 } else {
94 // type not supported for extraction
95 static_assert(util::Unsupported<DecayedType>);
96 }
97
98 return output;
99}
100
101struct Result : public ManagedObject<CassResult const> {
102 /* implicit */ Result(CassResult const* ptr);
103
104 [[nodiscard]] std::size_t
105 numRows() const;
106
107 [[nodiscard]] bool
108 hasRows() const;
109
110 template <typename... RowTypes>
111 std::optional<std::tuple<RowTypes...>>
112 get() const
113 requires(std::tuple_size<std::tuple<RowTypes...>>{} > 1)
114 {
115 // row managed internally by cassandra driver, hence no ManagedObject.
116 auto const* row = cass_result_first_row(*this);
117 if (row == nullptr)
118 return std::nullopt;
119
120 std::size_t idx = 0;
121 auto advanceId = [&idx]() { return idx++; };
122
123 return std::make_optional<std::tuple<RowTypes...>>(
124 {extractColumn<RowTypes>(row, advanceId())...}
125 );
126 }
127
128 template <typename RowType>
129 std::optional<RowType>
130 get() const
131 {
132 // row managed internally by cassandra driver, hence no ManagedObject.
133 auto const* row = cass_result_first_row(*this);
134 if (row == nullptr)
135 return std::nullopt;
136 return std::make_optional<RowType>(extractColumn<RowType>(row, 0));
137 }
138};
139
140class ResultIterator : public ManagedObject<CassIterator> {
141 bool hasMore_ = false;
142
143public:
144 /* implicit */ ResultIterator(CassIterator* ptr);
145
146 [[nodiscard]] static ResultIterator
147 fromResult(Result const& result);
148
149 [[maybe_unused]] bool
150 moveForward();
151
152 [[nodiscard]] bool
153 hasMore() const;
154
155 template <typename... RowTypes>
156 std::tuple<RowTypes...>
157 extractCurrentRow() const
158 {
159 // note: row is invalidated on each iteration.
160 // managed internally by cassandra driver, hence no ManagedObject.
161 auto const* row = cass_iterator_get_row(*this);
162
163 std::size_t idx = 0;
164 auto advanceId = [&idx]() { return idx++; };
165
166 return {extractColumn<RowTypes>(row, advanceId())...};
167 }
168};
169
170template <typename... Types>
171class ResultExtractor {
172 std::reference_wrapper<Result const> ref_;
173
174public:
175 struct Sentinel {};
176
177 struct Iterator {
178 using iterator_category = std::input_iterator_tag;
179 using difference_type = std::size_t; // rows count
180 using value_type = std::tuple<Types...>;
181
182 /* implicit */ Iterator(ResultIterator iterator) : iterator_{std::move(iterator)}
183 {
184 }
185
186 Iterator(Iterator const&) = delete;
187 Iterator&
188 operator=(Iterator const&) = delete;
189
190 value_type
191 operator*() const
192 {
193 return iterator_.extractCurrentRow<Types...>();
194 }
195
196 value_type
197 operator->()
198 {
199 return iterator_.extractCurrentRow<Types...>();
200 }
201
202 Iterator&
203 operator++()
204 {
205 iterator_.moveForward();
206 return *this;
207 }
208
209 bool
210 operator==(Sentinel const&) const
211 {
212 return not iterator_.hasMore();
213 }
214
215 private:
216 ResultIterator iterator_;
217 };
218
219 ResultExtractor(Result const& result) : ref_{std::cref(result)}
220 {
221 }
222
223 Iterator
224 begin()
225 {
226 return ResultIterator::fromResult(ref_);
227 }
228
229 Sentinel
230 end()
231 {
232 return {};
233 }
234};
235
236} // namespace data::cassandra::impl
Definition Result.hpp:140
static constexpr bool Unsupported
used for compile time checking of unsupported types
Definition UnsupportedType.hpp:7
Definition Result.hpp:101