Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
Statement.hpp
1#pragma once
2
3#include "data/cassandra/Types.hpp"
4#include "data/cassandra/impl/Collection.hpp"
5#include "data/cassandra/impl/ManagedObject.hpp"
6#include "data/cassandra/impl/Tuple.hpp"
7#include "util/UnsupportedType.hpp"
8
9#include <boost/uuid/uuid.hpp>
10#include <boost/uuid/uuid_io.hpp>
11#include <cassandra.h>
12#include <fmt/format.h>
13#include <xrpl/basics/base_uint.h>
14#include <xrpl/protocol/AccountID.h>
15#include <xrpl/protocol/STAccount.h>
16
17#include <cstddef>
18#include <cstdint>
19#include <stdexcept>
20#include <string>
21#include <string_view>
22#include <tuple>
23#include <type_traits>
24#include <vector>
25
26namespace data::cassandra::impl {
27
28class Statement : public ManagedObject<CassStatement> {
29 static constexpr auto kDeleter = [](CassStatement* ptr) { cass_statement_free(ptr); };
30
31public:
38 template <typename... Args>
39 explicit Statement(std::string_view query, Args&&... args)
40 : ManagedObject{cass_statement_new_n(query.data(), query.size(), sizeof...(args)), kDeleter}
41 {
42 // TODO: figure out how to set consistency level in config
43 // NOTE: Keyspace doesn't support QUORUM at write level
44 // cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
45 cass_statement_set_is_idempotent(*this, cass_true);
46 bind<Args...>(std::forward<Args>(args)...);
47 }
48
49 /* implicit */ Statement(CassStatement* ptr) : ManagedObject{ptr, kDeleter}
50 {
51 // cass_statement_set_consistency(*this, CASS_CONSISTENCY_LOCAL_QUORUM);
52 cass_statement_set_is_idempotent(*this, cass_true);
53 }
54
60 template <typename... Args>
61 void
62 bind(Args&&... args) const
63 {
64 std::size_t idx = 0; // NOLINT(misc-const-correctness)
65 (this->bindAt<Args>(idx++, std::forward<Args>(args)), ...);
66 }
67
74 template <typename Type>
75 void
76 bindAt(std::size_t const idx, Type&& value) const
77 {
78 using std::to_string;
79 auto throwErrorIfNeeded = [idx](CassError rc, std::string_view label) {
80 if (rc != CASS_OK) {
81 throw std::logic_error(
82 fmt::format("[{}] at idx {}: {}", label, idx, cass_error_desc(rc))
83 );
84 }
85 };
86
87 auto bindBytes = [this, idx](auto const* data, size_t size) {
88 return cass_statement_bind_bytes(
89 *this, idx, static_cast<cass_byte_t const*>(data), size
90 );
91 };
92
93 using DecayedType = std::decay_t<Type>;
94 using UCharVectorType = std::vector<unsigned char>;
95 using UintTupleType = std::tuple<uint32_t, uint32_t>;
96 using UintByteTupleType = std::tuple<uint32_t, xrpl::uint256>;
97 using ByteVectorType = std::vector<xrpl::uint256>;
98
99 if constexpr (
100 std::is_same_v<DecayedType, xrpl::uint256> || std::is_same_v<DecayedType, xrpl::uint192>
101 ) {
102 auto const rc = bindBytes(value.data(), value.size());
103 throwErrorIfNeeded(rc, "Bind xrpl::base_uint");
104 } else if constexpr (std::is_same_v<DecayedType, xrpl::AccountID>) {
105 auto const rc = bindBytes(value.data(), value.size());
106 throwErrorIfNeeded(rc, "Bind xrpl::AccountID");
107 } else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
108 auto const rc = bindBytes(value.data(), value.size());
109 throwErrorIfNeeded(rc, "Bind vector<unsigned char>");
110 } else if constexpr (std::is_convertible_v<DecayedType, std::string>) {
111 // reinterpret_cast is needed here :'(
112 auto const rc =
113 bindBytes(reinterpret_cast<unsigned char const*>(value.data()), value.size());
114 throwErrorIfNeeded(rc, "Bind string (as bytes)");
115 } else if constexpr (std::is_convertible_v<DecayedType, Text>) {
116 auto const rc =
117 cass_statement_bind_string_n(*this, idx, value.text.c_str(), value.text.size());
118 throwErrorIfNeeded(rc, "Bind string (as TEXT)");
119 } else if constexpr (
120 std::is_same_v<DecayedType, UintTupleType> ||
121 std::is_same_v<DecayedType, UintByteTupleType>
122 ) {
123 auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
124 throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, xrpl::uint256>");
125 } else if constexpr (std::is_same_v<DecayedType, ByteVectorType>) {
126 auto const rc =
127 cass_statement_bind_collection(*this, idx, Collection{std::forward<Type>(value)});
128 throwErrorIfNeeded(rc, "Bind collection");
129 } else if constexpr (std::is_same_v<DecayedType, bool>) {
130 auto const rc = cass_statement_bind_bool(*this, idx, value ? cass_true : cass_false);
131 throwErrorIfNeeded(rc, "Bind bool");
132 } else if constexpr (std::is_same_v<DecayedType, Limit>) {
133 auto const rc = cass_statement_bind_int32(*this, idx, value.limit);
134 throwErrorIfNeeded(rc, "Bind limit (int32)");
135 } else if constexpr (std::is_convertible_v<DecayedType, boost::uuids::uuid>) {
136 auto const uuidStr = boost::uuids::to_string(value);
137 CassUuid cassUuid;
138 auto rc = cass_uuid_from_string(uuidStr.c_str(), &cassUuid);
139 throwErrorIfNeeded(rc, "CassUuid from string");
140 rc = cass_statement_bind_uuid(*this, idx, cassUuid);
141 throwErrorIfNeeded(rc, "Bind boost::uuid");
142 // clio only uses bigint (int64_t) so we convert any incoming type
143 } else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
144 auto const rc = cass_statement_bind_int64(*this, idx, value);
145 throwErrorIfNeeded(rc, "Bind int64");
146 } else {
147 // type not supported for binding
148 static_assert(util::Unsupported<DecayedType>);
149 }
150 }
151};
152
158class PreparedStatement : public ManagedObject<CassPrepared const> {
159 static constexpr auto kDeleter = [](CassPrepared const* ptr) { cass_prepared_free(ptr); };
160
161public:
162 /* implicit */ PreparedStatement(CassPrepared const* ptr) : ManagedObject{ptr, kDeleter}
163 {
164 }
165
172 template <typename... Args>
173 [[nodiscard]] Statement
174 bind(Args&&... args) const
175 {
176 Statement statement = cass_prepared_bind(*this);
177 statement.bind<Args...>(std::forward<Args>(args)...);
178 return statement;
179 }
180};
181
182} // namespace data::cassandra::impl
Definition Collection.hpp:16
Definition ManagedObject.hpp:9
Statement bind(Args &&... args) const
Bind the given arguments and produce a ready to execute Statement.
Definition Statement.hpp:174
Definition Statement.hpp:28
Statement(std::string_view query, Args &&... args)
Construct a new statement with optionally provided arguments.
Definition Statement.hpp:39
void bind(Args &&... args) const
Binds the given arguments to the statement.
Definition Statement.hpp:62
void bindAt(std::size_t const idx, Type &&value) const
Binds an argument to a specific index.
Definition Statement.hpp:76
Definition Tuple.hpp:20
This namespace implements the data access layer and related components.
Definition AmendmentCenter.cpp:56
static constexpr bool Unsupported
used for compile time checking of unsupported types
Definition UnsupportedType.hpp:9