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