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