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(fmt::format("[{}] at idx {}: {}", label, idx, cass_error_desc(rc)));
101 };
102
103 auto bindBytes = [this, idx](auto const* data, size_t size) {
104 return cass_statement_bind_bytes(*this, idx, static_cast<cass_byte_t const*>(data), size);
105 };
106
107 using DecayedType = std::decay_t<Type>;
108 using UCharVectorType = std::vector<unsigned char>;
109 using UintTupleType = std::tuple<uint32_t, uint32_t>;
110 using UintByteTupleType = std::tuple<uint32_t, ripple::uint256>;
111 using ByteVectorType = std::vector<ripple::uint256>;
112
113 if constexpr (std::is_same_v<DecayedType, ripple::uint256> || std::is_same_v<DecayedType, ripple::uint192>) {
114 auto const rc = bindBytes(value.data(), value.size());
115 throwErrorIfNeeded(rc, "Bind ripple::base_uint");
116 } else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
117 auto const rc = bindBytes(value.data(), value.size());
118 throwErrorIfNeeded(rc, "Bind ripple::AccountID");
119 } else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
120 auto const rc = bindBytes(value.data(), value.size());
121 throwErrorIfNeeded(rc, "Bind vector<unsigned char>");
122 } else if constexpr (std::is_convertible_v<DecayedType, std::string>) {
123 // reinterpret_cast is needed here :'(
124 auto const rc = bindBytes(reinterpret_cast<unsigned char const*>(value.data()), value.size());
125 throwErrorIfNeeded(rc, "Bind string (as bytes)");
126 } else if constexpr (std::is_convertible_v<DecayedType, Text>) {
127 auto const rc = cass_statement_bind_string_n(*this, idx, value.text.c_str(), value.text.size());
128 throwErrorIfNeeded(rc, "Bind string (as TEXT)");
129 } else if constexpr (std::is_same_v<DecayedType, UintTupleType> ||
130 std::is_same_v<DecayedType, UintByteTupleType>) {
131 auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
132 throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, ripple::uint256>");
133 } else if constexpr (std::is_same_v<DecayedType, ByteVectorType>) {
134 auto const rc = cass_statement_bind_collection(*this, idx, Collection{std::forward<Type>(value)});
135 throwErrorIfNeeded(rc, "Bind collection");
136 } else if constexpr (std::is_same_v<DecayedType, bool>) {
137 auto const rc = cass_statement_bind_bool(*this, idx, value ? cass_true : cass_false);
138 throwErrorIfNeeded(rc, "Bind bool");
139 } else if constexpr (std::is_same_v<DecayedType, Limit>) {
140 auto const rc = cass_statement_bind_int32(*this, idx, value.limit);
141 throwErrorIfNeeded(rc, "Bind limit (int32)");
142 } else if constexpr (std::is_convertible_v<DecayedType, boost::uuids::uuid>) {
143 auto const uuidStr = boost::uuids::to_string(value);
144 CassUuid cassUuid;
145 auto rc = cass_uuid_from_string(uuidStr.c_str(), &cassUuid);
146 throwErrorIfNeeded(rc, "CassUuid from string");
147 rc = cass_statement_bind_uuid(*this, idx, cassUuid);
148 throwErrorIfNeeded(rc, "Bind boost::uuid");
149 // clio only uses bigint (int64_t) so we convert any incoming type
150 } else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
151 auto const rc = cass_statement_bind_int64(*this, idx, value);
152 throwErrorIfNeeded(rc, "Bind int64");
153 } else {
154 // type not supported for binding
155 static_assert(util::Unsupported<DecayedType>);
156 }
157 }
158};
159
165class PreparedStatement : public ManagedObject<CassPrepared const> {
166 static constexpr auto kDELETER = [](CassPrepared const* ptr) { cass_prepared_free(ptr); };
167
168public:
169 /* implicit */ PreparedStatement(CassPrepared const* ptr) : ManagedObject{ptr, kDELETER}
170 {
171 }
172
179 template <typename... Args>
181 bind(Args&&... args) const
182 {
183 Statement statement = cass_prepared_bind(*this);
184 statement.bind<Args...>(std::forward<Args>(args)...);
185 return statement;
186 }
187};
188
189} // 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:181
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:70
static constexpr bool Unsupported
used for compile time checking of unsupported types
Definition UnsupportedType.hpp:26