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