mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
I started with really simple pre-commit hooks and will add more on top. Important files: - `.pre-commit-config.yaml` - the config for pre-commit - `.github/workflows/pre-commit.yml` - runs pre-commit hooks in branches and `develop` - `.github/workflows/pre-commit-autoupdate.yml` - autoupdates pre-commit hooks once in a month
345 lines
12 KiB
C++
345 lines
12 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of clio: https://github.com/XRPLF/clio
|
|
Copyright (c) 2024, the clio developers.
|
|
|
|
Permission to use, copy, modify, and distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#pragma once
|
|
|
|
#include "data/LedgerCacheInterface.hpp"
|
|
#include "data/cassandra/Handle.hpp"
|
|
#include "data/cassandra/Schema.hpp"
|
|
#include "data/cassandra/SettingsProvider.hpp"
|
|
#include "data/cassandra/Types.hpp"
|
|
#include "migration/cassandra/CassandraMigrationBackend.hpp"
|
|
|
|
#include <boost/asio/spawn.hpp>
|
|
#include <fmt/core.h>
|
|
#include <xrpl/basics/base_uint.h>
|
|
|
|
#include <cstdint>
|
|
#include <exception>
|
|
#include <optional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
/**
|
|
* @brief Test backend for Cassandra migration. The class is mainly to provide an example of how to add the needed
|
|
* backend for the migrator. It is used in integration tests to provide the backend for the example migrators. In
|
|
* production, the backend code should be added to CassandraMigrationBackend directly.
|
|
*/
|
|
class CassandraMigrationTestBackend : public migration::cassandra::CassandraMigrationBackend {
|
|
data::cassandra::SettingsProvider settingsProvider_;
|
|
|
|
public:
|
|
/**
|
|
* @brief Construct a new Cassandra Migration Test Backend object
|
|
*
|
|
* @param settingsProvider The settings provider for the Cassandra backend
|
|
* @param cache The ledger cache to use
|
|
*/
|
|
CassandraMigrationTestBackend(data::cassandra::SettingsProvider settingsProvider, data::LedgerCacheInterface& cache)
|
|
: migration::cassandra::CassandraMigrationBackend(settingsProvider, cache)
|
|
, settingsProvider_(std::move(settingsProvider))
|
|
|
|
{
|
|
if (auto const res = handle_.executeEach(createTablesSchema()); not res)
|
|
throw std::runtime_error("Could not create schema: " + res.error());
|
|
}
|
|
|
|
/**
|
|
* @brief Write a transaction hash and its transaction type to the tx_index_example table. It's used by
|
|
* ExampleTransactionsMigrator.
|
|
*
|
|
* @param hash The transaction hash
|
|
* @param txType The transaction type
|
|
*/
|
|
void
|
|
writeTxIndexExample(std::string const& hash, std::string const& txType)
|
|
{
|
|
auto static kINSERT_TX_INDEX_EXAMPLE = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
INSERT INTO {}
|
|
(hash, tx_type)
|
|
VALUES (?, ?)
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "tx_index_example")
|
|
));
|
|
}();
|
|
executor_.writeSync(kINSERT_TX_INDEX_EXAMPLE.bind(hash, data::cassandra::Text(txType)));
|
|
}
|
|
|
|
/**
|
|
* @brief Fetch the transaction type via transaction hash from the tx_index_example table. It's used by
|
|
* ExampleTransactionsMigrator validation.
|
|
*
|
|
* @param hash The transaction hash
|
|
* @param ctx The boost asio context
|
|
* @return The transaction type if found, otherwise std::nullopt
|
|
*/
|
|
std::optional<std::string>
|
|
fetchTxTypeViaID(std::string const& hash, boost::asio::yield_context ctx)
|
|
{
|
|
auto static kFETCH_TX_TYPE = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
SELECT tx_type FROM {} WHERE hash = ?
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "tx_index_example")
|
|
));
|
|
}();
|
|
auto const res = executor_.read(ctx, kFETCH_TX_TYPE.bind(hash));
|
|
if (not res) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto const& result = res.value();
|
|
if (not result.hasRows()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto const& [txType] : data::cassandra::extract<std::string>(result)) {
|
|
return txType;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Fetch the transaction index table size. It's used by ExampleTransactionsMigrator validation.
|
|
*
|
|
* @param ctx The boost asio context
|
|
* @return The size of the transaction index table if found, otherwise std::nullopt
|
|
*/
|
|
std::optional<std::uint64_t>
|
|
fetchTxIndexTableSize(boost::asio::yield_context ctx)
|
|
{
|
|
auto static kINSERT_TX_INDEX_EXAMPLE = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
SELECT COUNT(*) FROM {}
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "tx_index_example")
|
|
));
|
|
}();
|
|
|
|
// This function will be called after table being dropped, catch the exception
|
|
try {
|
|
auto const res = executor_.read(ctx, kINSERT_TX_INDEX_EXAMPLE);
|
|
if (not res) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto const& result = res.value();
|
|
if (not result.hasRows()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto const& [size] : data::cassandra::extract<std::uint64_t>(result)) {
|
|
return size;
|
|
}
|
|
} catch (std::exception& e) {
|
|
return std::nullopt;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
*@brief Write the ledger account hash to the ledger_example table. It's used by ExampleLedgerMigrator.
|
|
*
|
|
* @param sequence The ledger sequence
|
|
* @param accountHash The account hash
|
|
*/
|
|
void
|
|
writeLedgerAccountHash(std::uint64_t sequence, std::string const& accountHash)
|
|
{
|
|
auto static kINSERT_LEDGER_EXAMPLE = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
INSERT INTO {}
|
|
(sequence, account_hash)
|
|
VALUES (?, ?)
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "ledger_example")
|
|
));
|
|
}();
|
|
executor_.writeSync(kINSERT_LEDGER_EXAMPLE.bind(sequence, accountHash));
|
|
}
|
|
|
|
/**
|
|
* @brief Fetch the account hash via ledger sequence from the ledger_example table. It's used by
|
|
* ExampleLedgerMigrator validation.
|
|
*
|
|
* @param sequence The ledger sequence
|
|
* @param ctx The boost asio context
|
|
* @return The account hash if found, otherwise std::nullopt
|
|
*/
|
|
std::optional<ripple::uint256>
|
|
fetchAccountHashViaSequence(std::uint64_t sequence, boost::asio::yield_context ctx)
|
|
{
|
|
auto static kFETCH_ACCOUNT_HASH = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
SELECT account_hash FROM {} WHERE sequence = ?
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "ledger_example")
|
|
));
|
|
}();
|
|
auto const res = executor_.read(ctx, kFETCH_ACCOUNT_HASH.bind(sequence));
|
|
if (not res) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto const& result = res.value();
|
|
if (not result.hasRows()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto const& [accountHash] : data::cassandra::extract<ripple::uint256>(result)) {
|
|
return accountHash;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Fetch the ledger example table size. It's used by ExampleLedgerMigrator validation.
|
|
*
|
|
* @param ctx The boost asio context
|
|
* @return The size of the ledger example table if found, otherwise std::nullopt
|
|
*/
|
|
std::optional<std::uint64_t>
|
|
fetchLedgerTableSize(boost::asio::yield_context ctx)
|
|
{
|
|
auto static kINSERT_LEDGER_EXAMPLE = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
SELECT COUNT(*) FROM {}
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "ledger_example")
|
|
));
|
|
}();
|
|
|
|
// This function will be called after table being dropped, catch the exception
|
|
try {
|
|
auto const res = executor_.read(ctx, kINSERT_LEDGER_EXAMPLE);
|
|
if (not res) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto const& result = res.value();
|
|
if (not result.hasRows()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto const& [size] : data::cassandra::extract<std::uint64_t>(result)) {
|
|
return size;
|
|
}
|
|
} catch (std::exception& e) {
|
|
return std::nullopt;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/**
|
|
* @brief Drop the diff table. It's used by ExampleDropTableMigrator.
|
|
*
|
|
* @return The result of the operation
|
|
*/
|
|
auto
|
|
dropDiffTable()
|
|
{
|
|
return handle_.execute(fmt::format(
|
|
R"(
|
|
DROP TABLE IF EXISTS {}
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "diff")
|
|
));
|
|
}
|
|
|
|
/**
|
|
* @brief Fetch the diff table size. It's used by ExampleDropTableMigrator validation.
|
|
*
|
|
* @param ctx The boost asio context
|
|
* @return The size of the diff table if found, otherwise std::nullopt
|
|
*/
|
|
std::optional<std::uint64_t>
|
|
fetchDiffTableSize(boost::asio::yield_context ctx)
|
|
{
|
|
auto static kCOUNT_DIFF = [this]() {
|
|
return handle_.prepare(fmt::format(
|
|
R"(
|
|
SELECT COUNT(*) FROM {}
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "diff")
|
|
));
|
|
}();
|
|
|
|
// This function will be called after table being dropped, catch the exception
|
|
try {
|
|
auto const res = executor_.read(ctx, kCOUNT_DIFF);
|
|
if (not res) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
auto const& result = res.value();
|
|
if (not result.hasRows()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
for (auto const& [size] : data::cassandra::extract<std::uint64_t>(result)) {
|
|
return size;
|
|
}
|
|
} catch (std::exception& e) {
|
|
return std::nullopt;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
private:
|
|
std::vector<data::cassandra::Statement>
|
|
createTablesSchema()
|
|
{
|
|
std::vector<data::cassandra::Statement> statements;
|
|
|
|
statements.emplace_back(fmt::format(
|
|
R"(
|
|
CREATE TABLE IF NOT EXISTS {}
|
|
(
|
|
hash blob,
|
|
tx_type text,
|
|
PRIMARY KEY (hash)
|
|
)
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "tx_index_example")
|
|
));
|
|
|
|
statements.emplace_back(fmt::format(
|
|
R"(
|
|
CREATE TABLE IF NOT EXISTS {}
|
|
(
|
|
sequence bigint,
|
|
account_hash blob,
|
|
PRIMARY KEY (sequence)
|
|
)
|
|
)",
|
|
data::cassandra::qualifiedTableName(settingsProvider_, "ledger_example")
|
|
));
|
|
return statements;
|
|
}
|
|
};
|