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, ripple::uint256>;
97 using ByteVectorType = std::vector<ripple::uint256>;
98
99 if constexpr (
100 std::is_same_v<DecayedType, ripple::uint256> ||
101 std::is_same_v<DecayedType, ripple::uint192>
102 ) {
103 auto const rc = bindBytes(value.data(), value.size());
104 throwErrorIfNeeded(rc, "Bind ripple::base_uint");
105 } else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
106 auto const rc = bindBytes(value.data(), value.size());
107 throwErrorIfNeeded(rc, "Bind ripple::AccountID");
108 } else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
109 auto const rc = bindBytes(value.data(), value.size());
110 throwErrorIfNeeded(rc, "Bind vector<unsigned char>");
111 } else if constexpr (std::is_convertible_v<DecayedType, std::string>) {
112 // reinterpret_cast is needed here :'(
113 auto const rc =
114 bindBytes(reinterpret_cast<unsigned char const*>(value.data()), value.size());
115 throwErrorIfNeeded(rc, "Bind string (as bytes)");
116 } else if constexpr (std::is_convertible_v<DecayedType, Text>) {
117 auto const rc =
118 cass_statement_bind_string_n(*this, idx, value.text.c_str(), value.text.size());
119 throwErrorIfNeeded(rc, "Bind string (as TEXT)");
120 } else if constexpr (
121 std::is_same_v<DecayedType, UintTupleType> ||
122 std::is_same_v<DecayedType, UintByteTupleType>
123 ) {
124 auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
125 throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, ripple::uint256>");
126 } else if constexpr (std::is_same_v<DecayedType, ByteVectorType>) {
127 auto const rc =
128 cass_statement_bind_collection(*this, idx, Collection{std::forward<Type>(value)});
129 throwErrorIfNeeded(rc, "Bind collection");
130 } else if constexpr (std::is_same_v<DecayedType, bool>) {
131 auto const rc = cass_statement_bind_bool(*this, idx, value ? cass_true : cass_false);
132 throwErrorIfNeeded(rc, "Bind bool");
133 } else if constexpr (std::is_same_v<DecayedType, Limit>) {
134 auto const rc = cass_statement_bind_int32(*this, idx, value.limit);
135 throwErrorIfNeeded(rc, "Bind limit (int32)");
136 } else if constexpr (std::is_convertible_v<DecayedType, boost::uuids::uuid>) {
137 auto const uuidStr = boost::uuids::to_string(value);
138 CassUuid cassUuid;
139 auto rc = cass_uuid_from_string(uuidStr.c_str(), &cassUuid);
140 throwErrorIfNeeded(rc, "CassUuid from string");
141 rc = cass_statement_bind_uuid(*this, idx, cassUuid);
142 throwErrorIfNeeded(rc, "Bind boost::uuid");
143 // clio only uses bigint (int64_t) so we convert any incoming type
144 } else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
145 auto const rc = cass_statement_bind_int64(*this, idx, value);
146 throwErrorIfNeeded(rc, "Bind int64");
147 } else {
148 // type not supported for binding
149 static_assert(util::Unsupported<DecayedType>);
150 }
151 }
152};
153
159class PreparedStatement : public ManagedObject<CassPrepared const> {
160 static constexpr auto kDELETER = [](CassPrepared const* ptr) { cass_prepared_free(ptr); };
161
162public:
163 /* implicit */ PreparedStatement(CassPrepared const* ptr) : ManagedObject{ptr, kDELETER}
164 {
165 }
166
173 template <typename... Args>
175 bind(Args&&... args) const
176 {
177 Statement statement = cass_prepared_bind(*this);
178 statement.bind<Args...>(std::forward<Args>(args)...);
179 return statement;
180 }
181};
182
183} // 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:175
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:7