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...>>({extractColumn<RowTypes>(row, advanceId())...});
143 }
144
145 template <typename RowType>
146 std::optional<RowType>
147 get() const
148 {
149 // row managed internally by cassandra driver, hence no ManagedObject.
150 auto const* row = cass_result_first_row(*this);
151 if (row == nullptr)
152 return std::nullopt;
153 return std::make_optional<RowType>(extractColumn<RowType>(row, 0));
154 }
155};
156
157class ResultIterator : public ManagedObject<CassIterator> {
158 bool hasMore_ = false;
159
160public:
161 /* implicit */ ResultIterator(CassIterator* ptr);
162
163 [[nodiscard]] static ResultIterator
164 fromResult(Result const& result);
165
166 [[maybe_unused]] bool
167 moveForward();
168
169 [[nodiscard]] bool
170 hasMore() const;
171
172 template <typename... RowTypes>
173 std::tuple<RowTypes...>
174 extractCurrentRow() const
175 {
176 // note: row is invalidated on each iteration.
177 // managed internally by cassandra driver, hence no ManagedObject.
178 auto const* row = cass_iterator_get_row(*this);
179
180 std::size_t idx = 0;
181 auto advanceId = [&idx]() { return idx++; };
182
183 return {extractColumn<RowTypes>(row, advanceId())...};
184 }
185};
186
187template <typename... Types>
189 std::reference_wrapper<Result const> ref_;
190
191public:
192 struct Sentinel {};
193
194 struct Iterator {
195 using iterator_category = std::input_iterator_tag;
196 using difference_type = std::size_t; // rows count
197 using value_type = std::tuple<Types...>;
198
199 /* implicit */ Iterator(ResultIterator iterator) : iterator_{std::move(iterator)}
200 {
201 }
202
203 Iterator(Iterator const&) = delete;
204 Iterator&
205 operator=(Iterator const&) = delete;
206
207 value_type
208 operator*() const
209 {
210 return iterator_.extractCurrentRow<Types...>();
211 }
212
213 value_type
214 operator->()
215 {
216 return iterator_.extractCurrentRow<Types...>();
217 }
218
219 Iterator&
220 operator++()
221 {
222 iterator_.moveForward();
223 return *this;
224 }
225
226 bool
227 operator==(Sentinel const&) const
228 {
229 return not iterator_.hasMore();
230 }
231
232 private:
233 ResultIterator iterator_;
234 };
235
236 ResultExtractor(Result const& result) : ref_{std::cref(result)}
237 {
238 }
239
240 Iterator
241 begin()
242 {
243 return ResultIterator::fromResult(ref_);
244 }
245
246 Sentinel
247 end()
248 {
249 return {};
250 }
251};
252
253} // namespace data::cassandra::impl
Definition ManagedObject.hpp:28
Definition Result.hpp:157
static constexpr bool Unsupported
used for compile time checking of unsupported types
Definition UnsupportedType.hpp:26
Definition Result.hpp:120