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/core.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 cass_statement_set_consistency(*this, CASS_CONSISTENCY_QUORUM);
62 cass_statement_set_is_idempotent(*this, cass_true);
63 bind<Args...>(std::forward<Args>(args)...);
64 }
65
66 /* implicit */ Statement(CassStatement* ptr) : ManagedObject{ptr, kDELETER}
67 {
68 cass_statement_set_consistency(*this, CASS_CONSISTENCY_QUORUM);
69 cass_statement_set_is_idempotent(*this, cass_true);
70 }
71
77 template <typename... Args>
78 void
79 bind(Args&&... args) const
80 {
81 std::size_t idx = 0; // NOLINT(misc-const-correctness)
82 (this->bindAt<Args>(idx++, std::forward<Args>(args)), ...);
83 }
84
91 template <typename Type>
92 void
93 bindAt(std::size_t const idx, Type&& value) const
94 {
95 using std::to_string;
96 auto throwErrorIfNeeded = [idx](CassError rc, std::string_view label) {
97 if (rc != CASS_OK)
98 throw std::logic_error(fmt::format("[{}] at idx {}: {}", label, idx, cass_error_desc(rc)));
99 };
100
101 auto bindBytes = [this, idx](auto const* data, size_t size) {
102 return cass_statement_bind_bytes(*this, idx, static_cast<cass_byte_t const*>(data), size);
103 };
104
105 using DecayedType = std::decay_t<Type>;
106 using UCharVectorType = std::vector<unsigned char>;
107 using UintTupleType = std::tuple<uint32_t, uint32_t>;
108 using UintByteTupleType = std::tuple<uint32_t, ripple::uint256>;
109 using ByteVectorType = std::vector<ripple::uint256>;
110
111 if constexpr (std::is_same_v<DecayedType, ripple::uint256> || std::is_same_v<DecayedType, ripple::uint192>) {
112 auto const rc = bindBytes(value.data(), value.size());
113 throwErrorIfNeeded(rc, "Bind ripple::base_uint");
114 } else if constexpr (std::is_same_v<DecayedType, ripple::AccountID>) {
115 auto const rc = bindBytes(value.data(), value.size());
116 throwErrorIfNeeded(rc, "Bind ripple::AccountID");
117 } else if constexpr (std::is_same_v<DecayedType, UCharVectorType>) {
118 auto const rc = bindBytes(value.data(), value.size());
119 throwErrorIfNeeded(rc, "Bind vector<unsigned char>");
120 } else if constexpr (std::is_convertible_v<DecayedType, std::string>) {
121 // reinterpret_cast is needed here :'(
122 auto const rc = bindBytes(reinterpret_cast<unsigned char const*>(value.data()), value.size());
123 throwErrorIfNeeded(rc, "Bind string (as bytes)");
124 } else if constexpr (std::is_convertible_v<DecayedType, Text>) {
125 auto const rc = cass_statement_bind_string_n(*this, idx, value.text.c_str(), value.text.size());
126 throwErrorIfNeeded(rc, "Bind string (as TEXT)");
127 } else if constexpr (std::is_same_v<DecayedType, UintTupleType> ||
128 std::is_same_v<DecayedType, UintByteTupleType>) {
129 auto const rc = cass_statement_bind_tuple(*this, idx, Tuple{std::forward<Type>(value)});
130 throwErrorIfNeeded(rc, "Bind tuple<uint32, uint32> or <uint32_t, ripple::uint256>");
131 } else if constexpr (std::is_same_v<DecayedType, ByteVectorType>) {
132 auto const rc = cass_statement_bind_collection(*this, idx, Collection{std::forward<Type>(value)});
133 throwErrorIfNeeded(rc, "Bind collection");
134 } else if constexpr (std::is_same_v<DecayedType, bool>) {
135 auto const rc = cass_statement_bind_bool(*this, idx, value ? cass_true : cass_false);
136 throwErrorIfNeeded(rc, "Bind bool");
137 } else if constexpr (std::is_same_v<DecayedType, Limit>) {
138 auto const rc = cass_statement_bind_int32(*this, idx, value.limit);
139 throwErrorIfNeeded(rc, "Bind limit (int32)");
140 } else if constexpr (std::is_convertible_v<DecayedType, boost::uuids::uuid>) {
141 auto const uuidStr = boost::uuids::to_string(value);
142 CassUuid cassUuid;
143 auto rc = cass_uuid_from_string(uuidStr.c_str(), &cassUuid);
144 throwErrorIfNeeded(rc, "CassUuid from string");
145 rc = cass_statement_bind_uuid(*this, idx, cassUuid);
146 throwErrorIfNeeded(rc, "Bind boost::uuid");
147 // clio only uses bigint (int64_t) so we convert any incoming type
148 } else if constexpr (std::is_convertible_v<DecayedType, int64_t>) {
149 auto const rc = cass_statement_bind_int64(*this, idx, value);
150 throwErrorIfNeeded(rc, "Bind int64");
151 } else {
152 // type not supported for binding
153 static_assert(util::Unsupported<DecayedType>);
154 }
155 }
156};
157
163class PreparedStatement : public ManagedObject<CassPrepared const> {
164 static constexpr auto kDELETER = [](CassPrepared const* ptr) { cass_prepared_free(ptr); };
165
166public:
167 /* implicit */ PreparedStatement(CassPrepared const* ptr) : ManagedObject{ptr, kDELETER}
168 {
169 }
170
177 template <typename... Args>
179 bind(Args&&... args) const
180 {
181 Statement statement = cass_prepared_bind(*this);
182 statement.bind<Args...>(std::forward<Args>(args)...);
183 return statement;
184 }
185};
186
187} // namespace data::cassandra::impl
Definition Collection.hpp:35
Definition ManagedObject.hpp:28
Represents a prepared statement on the DB side.
Definition Statement.hpp:163
Statement bind(Args &&... args) const
Bind the given arguments and produce a ready to execute Statement.
Definition Statement.hpp:179
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:79
void bindAt(std::size_t const idx, Type &&value) const
Binds an argument to a specific index.
Definition Statement.hpp:93
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