Use soci in more places:

* Validator, peerfinder, SHAMapStore,
  RpcDB, TxnDB, LedgerDB, WalletDB use soci backend.
This commit is contained in:
seelabs
2015-01-22 15:04:30 -08:00
committed by Vinnie Falco
parent d37802a42f
commit 97623d20c5
32 changed files with 1474 additions and 1122 deletions

View File

@@ -4305,8 +4305,6 @@
</ClCompile>
<ClInclude Include="..\..\src\snappy\snappy\snappy.h">
</ClInclude>
<ClInclude Include="..\..\src\soci\src\backends\postgresql\soci-postgresql.h">
</ClInclude>
<ClCompile Include="..\..\src\soci\src\backends\sqlite3\blob.cpp">
<ExcludedFromBuild>True</ExcludedFromBuild>
</ClCompile>

View File

@@ -535,9 +535,6 @@
<Filter Include="soci\src\backends">
<UniqueIdentifier>{AA927DBA-1AF8-6600-04B7-D1C1EBFB4103}</UniqueIdentifier>
</Filter>
<Filter Include="soci\src\backends\postgresql">
<UniqueIdentifier>{5D2927A9-CC6E-DDE0-1654-5316177082DD}</UniqueIdentifier>
</Filter>
<Filter Include="soci\src\backends\sqlite3">
<UniqueIdentifier>{75E6832F-A6F7-8360-FA3A-7427A06A9959}</UniqueIdentifier>
</Filter>
@@ -5040,9 +5037,6 @@
<ClInclude Include="..\..\src\snappy\snappy\snappy.h">
<Filter>snappy\snappy</Filter>
</ClInclude>
<ClInclude Include="..\..\src\soci\src\backends\postgresql\soci-postgresql.h">
<Filter>soci\src\backends\postgresql</Filter>
</ClInclude>
<ClCompile Include="..\..\src\soci\src\backends\sqlite3\blob.cpp">
<Filter>soci\src\backends\sqlite3</Filter>
</ClCompile>

View File

@@ -40,6 +40,26 @@ If the clang toolchain is detected, then the default target will use it, else
the gcc toolchain will be used. On Windows environments, the MSVC toolchain is
also detected.
The following environment variables modify the build environment:
CLANG_CC
CLANG_CXX
CLANG_LINK
If set, a clang toolchain will be used. These must all be set together.
GNU_CC
GNU_CXX
GNU_LINK
If set, a gcc toolchain will be used (unless a clang toolchain is
detected first). These must all be set together.
CXX
If set, used to detect a toolchain.
BOOST_ROOT
Path to the boost directory.
OPENSSL_ROOT
Path to the openssl directory.
'''
#
'''

View File

@@ -19,8 +19,9 @@
#include <BeastConfig.h>
#include <ripple/app/data/DatabaseCon.h>
#include <ripple/app/data/SqliteDatabase.h>
#include <ripple/core/ConfigSections.h>
#include <ripple/app/data/SociDB.h>
#include <ripple/basics/Log.h>
#include <beast/cxx14/memory.h> // <memory>
namespace ripple {
@@ -28,6 +29,7 @@ DatabaseCon::DatabaseCon (Setup const& setup,
std::string const& strName,
const char* initStrings[],
int initCount)
:session_(std::make_shared<soci::session>())
{
auto const useTempFiles // Use temporary files or regular DB files?
= setup.standAlone &&
@@ -37,26 +39,28 @@ DatabaseCon::DatabaseCon (Setup const& setup,
boost::filesystem::path pPath = useTempFiles
? "" : (setup.dataDir / strName);
mDatabase = new SqliteDatabase (pPath.string ().c_str ());
mDatabase->connect ();
open(*session_, "sqlite", pPath.string());
for (int i = 0; i < initCount; ++i)
mDatabase->executeSQL (initStrings[i], true);
{
try
{
*session_ << initStrings[i];
}
catch (soci::soci_error& e)
{
WriteLog (lsWARNING, DatabaseCon)
<< "Error executing initial statements for: " << strName
<< " error: " << e.what () << "\n"
<< " statement: " << initStrings[i];
}
}
}
DatabaseCon::~DatabaseCon ()
{
mDatabase->disconnect ();
delete mDatabase;
}
DatabaseCon::Setup
setup_DatabaseCon (Config const& c)
DatabaseCon::Setup setup_DatabaseCon (Config const& c)
{
DatabaseCon::Setup setup;
auto const& sec = c.section (ConfigSection::nodeDatabase ());
get_if_exists (sec, "online_delete", setup.onlineDelete);
setup.startUp = c.START_UP;
setup.standAlone = c.RUN_STANDALONE;
setup.dataDir = c.legacy ("database_path");
@@ -64,4 +68,8 @@ setup_DatabaseCon (Config const& c)
return setup;
}
void DatabaseCon::setupCheckpointing (JobQueue* q)
{
walCheckpointer_ = std::make_unique<WALCheckpointer> (session_, q);
}
} // ripple

View File

@@ -20,24 +20,65 @@
#ifndef RIPPLE_APP_DATA_DATABASECON_H_INCLUDED
#define RIPPLE_APP_DATA_DATABASECON_H_INCLUDED
#include <ripple/app/data/Database.h>
#include <ripple/core/Config.h>
#include <ripple/app/data/SociDB.h>
#include <boost/filesystem/path.hpp>
#include <mutex>
#include <string>
namespace soci {
class session;
}
namespace ripple {
class Database;
template<class T, class TMutex>
class LockedPointer
{
public:
using mutex = TMutex;
private:
T* it_;
std::unique_lock<mutex> lock_;
public:
LockedPointer (T* it, mutex& m) : it_ (it), lock_ (m)
{
}
LockedPointer (LockedPointer&& rhs) noexcept
: it_ (rhs.it_), lock_ (std::move (rhs.lock_))
{
}
LockedPointer () = delete;
LockedPointer (LockedPointer const& rhs) = delete;
LockedPointer& operator=(LockedPointer const& rhs) = delete;
T* get ()
{
return it_;
}
T& operator*()
{
return *it_;
}
T* operator->()
{
return it_;
}
explicit operator bool() const
{
return bool(it_);
}
};
using LockedSociSession = LockedPointer<soci::session, std::recursive_mutex>;
// VFALCO NOTE This looks like a pointless class. Figure out
// what purpose it is really trying to serve and do it better.
class DatabaseCon
{
public:
struct Setup
{
bool onlineDelete = false;
Config::StartUpType startUp = Config::NORMAL;
bool standAlone = false;
boost::filesystem::path dataDir;
@@ -47,32 +88,26 @@ public:
std::string const& name,
const char* initString[],
int countInit);
~DatabaseCon ();
Database* getDB ()
soci::session& getSession()
{
return mDatabase;
return *session_;
}
typedef std::recursive_mutex mutex;
std::unique_lock<mutex> lock ()
LockedSociSession checkoutDb()
{
return std::unique_lock<mutex>(mLock);
}
mutex& peekMutex()
{
return mLock;
return LockedSociSession(session_.get (), lock_);
}
void setupCheckpointing (JobQueue*);
private:
Database* mDatabase;
mutex mLock;
std::unique_ptr<WALCheckpointer> walCheckpointer_;
// shared_ptr to handle lifetime issues with walCheckpointer_
// never null.
std::shared_ptr<soci::session> session_;
LockedSociSession::mutex lock_;
};
//------------------------------------------------------------------------------
DatabaseCon::Setup
setup_DatabaseCon (Config const& c);

View File

@@ -17,11 +17,11 @@
*/
//==============================================================================
/** Stub functions for soci dynamic backends.
/* Stub functions for soci dynamic backends.
Ripple does not use dynamic backends, and inclduing soci's
dynamic backends compilcates the build (it requires a generated
header file and some macros to be defines.)
header file and some macros to be defined.)
*/
#include <BeastConfig.h>
@@ -35,8 +35,6 @@ namespace dynamic_backends {
backend_factory const& get (std::string const& name)
{
throw std::runtime_error ("Not Supported");
backend_factory* nullBF{nullptr};
return *nullBF; // deref nullptr - but we already threw
};
// provided for advanced user-level management

View File

@@ -17,14 +17,6 @@
*/
//==============================================================================
/** An embedded database wrapper with an intuitive, type-safe interface.
This collection of classes let's you access embedded SQLite databases
using C++ syntax that is very similar to regular SQL.
This module requires the @ref beast_sqlite external module.
*/
#include <BeastConfig.h>
#include <ripple/core/ConfigSections.h>
@@ -32,9 +24,6 @@
#include <ripple/core/Config.h>
#include <beast/cxx14/memory.h> // <memory>
#include <backends/sqlite3/soci-sqlite3.h>
#if ENABLE_SOCI_POSTGRESQL
#include <backends/postgresql/soci-postgresql.h>
#endif
#include <boost/filesystem.hpp>
namespace ripple {
@@ -57,69 +46,12 @@ getSociSqliteInit (std::string const& name,
return std::make_pair (file.string (), std::ref(soci::sqlite3));
}
#if ENABLE_SOCI_POSTGRESQL
std::pair<std::string, soci::backend_factory const&>
getSociPostgresqlInit (Section const& configSection,
std::string const& name)
{
if (name.empty ())
{
throw std::runtime_error (
"Missing required value for postgresql backend: database name");
}
std::string const host(get <std::string> (configSection, "host", ""));
if (!host.empty())
{
throw std::runtime_error (
"Missing required value in config for postgresql backend: host");
}
std::string const user(get <std::string> (configSection, "user", ""));
if (user.empty ())
{
throw std::runtime_error (
"Missing required value in config for postgresql backend: user");
}
int const port = [&configSection]
{
std::string const portAsString (
get <std::string> (configSection, "port", ""));
if (portAsString.empty ())
{
throw std::runtime_error (
"Missing required value in config for postgresql backend: "
"user");
}
try
{
return std::stoi (portAsString);
}
catch (...)
{
throw std::runtime_error (
"The port value in the config for the postgresql backend must "
"be an integer. Got: " +
portAsString);
}
}();
std::stringstream s;
s << "host=" << host << " port=" << port << " dbname=" << name
<< " user=" << user;
return std::make_pair (s.str (), std::ref(soci::postgresql));
}
#endif // ENABLE_SOCI_POSTGRESQL
std::pair<std::string, soci::backend_factory const&>
getSociInit (BasicConfig const& config,
std::string const& dbName)
{
static const std::string sectionName ("sqdb");
static const std::string keyName ("backend");
auto const& section = config.section (sectionName);
std::string const backendName(get(section, keyName, std::string("sqlite")));
auto const& section = config.section ("sqdb");
std::string const backendName(get(section, "backend", "sqlite"));
if (backendName == "sqlite")
{
@@ -129,12 +61,6 @@ getSociInit (BasicConfig const& config,
: ".db";
return detail::getSociSqliteInit(dbName, path, ext);
}
#if ENABLE_SOCI_POSTGRESQL
else if (backendName == "postgresql")
{
return detail::getSociPostgresqlInit(section, dbName);
}
#endif
else
{
throw std::runtime_error ("Unsupported soci backend: " + backendName);
@@ -142,13 +68,14 @@ getSociInit (BasicConfig const& config,
}
} // detail
SociConfig::SociConfig(std::pair<std::string, soci::backend_factory const&> init)
:connectionString_(std::move(init.first)),
backendFactory_(init.second){}
SociConfig::SociConfig (std::pair<std::string, soci::backend_factory const&> init)
: connectionString_ (std::move (init.first)),
backendFactory_ (init.second)
{
}
SociConfig::SociConfig(BasicConfig const& config,
std::string const& dbName)
: SociConfig(detail::getSociInit(config, dbName))
SociConfig::SociConfig (BasicConfig const& config, std::string const& dbName)
: SociConfig (detail::getSociInit (config, dbName))
{
}
@@ -169,5 +96,160 @@ void open(soci::session& s,
SociConfig c(config, dbName);
c.open(s);
}
void open(soci::session& s,
std::string const& beName,
std::string const& connectionString)
{
if (beName == "sqlite")
{
s.open(soci::sqlite3, connectionString);
return;
}
else
{
throw std::runtime_error ("Unsupported soci backend: " + beName);
}
}
size_t getKBUsedAll (soci::session& s)
{
auto be = dynamic_cast<soci::sqlite3_session_backend*>(s.get_backend ());
assert (be); // Make sure the backend is sqlite
return static_cast<int>(sqlite_api::sqlite3_memory_used () / 1024);
}
size_t getKBUsedDB (soci::session& s)
{
// This function will have to be customized when other backends are added
auto be = dynamic_cast<soci::sqlite3_session_backend*>(s.get_backend ());
assert (be);
int cur = 0, hiw = 0;
sqlite_api::sqlite3_db_status (
be->conn_, SQLITE_DBSTATUS_CACHE_USED, &cur, &hiw, 0);
return cur / 1024;
}
void convert(soci::blob& from, std::vector<std::uint8_t>& to)
{
to.resize (from.get_len ());
if (to.empty ())
return;
from.read (0, reinterpret_cast<char*>(&to[0]), from.get_len ());
}
void convert(soci::blob& from, std::string& to)
{
std::vector<std::uint8_t> tmp;
convert(from, tmp);
to.assign(tmp.begin (), tmp.end());
}
void convert(std::vector<std::uint8_t> const& from, soci::blob& to)
{
if (!from.empty ())
to.write (0, reinterpret_cast<char const*>(&from[0]), from.size ());
}
int SqliteWALHook (void* s,
sqlite_api::sqlite3*,
const char* dbName,
int walSize)
{
(reinterpret_cast<WALCheckpointer*>(s))->doHook (dbName, walSize);
return SQLITE_OK;
}
WALCheckpointer::WALCheckpointer (std::shared_ptr<soci::session> const& s,
JobQueue* q)
: Thread ("sqlitedb"), session_(s)
{
if (auto session =
dynamic_cast<soci::sqlite3_session_backend*>(s->get_backend ()))
conn_ = session->conn_;
if (!conn_) return;
startThread ();
setupCheckpointing (q);
}
WALCheckpointer::~WALCheckpointer ()
{
if (!conn_) return;
stopThread ();
}
void WALCheckpointer::setupCheckpointing (JobQueue* q)
{
if (!conn_) return;
q_ = q;
sqlite_api::sqlite3_wal_hook (conn_, SqliteWALHook, this);
}
void WALCheckpointer::doHook (const char* db, int pages)
{
if (!conn_) return;
if (pages < 1000)
return;
{
ScopedLockType sl (mutex_);
if (running_)
return;
running_ = true;
}
if (q_)
q_->addJob (jtWAL,
std::string ("WAL"),
std::bind (&WALCheckpointer::runWal, this));
else
notify ();
}
void WALCheckpointer::run ()
{
if (!conn_) return;
// Simple thread loop runs Wal every time it wakes up via
// the call to Thread::notify, unless Thread::threadShouldExit returns
// true in which case we simply break.
//
for (;;)
{
wait ();
if (threadShouldExit ())
break;
runWal ();
}
}
void WALCheckpointer::runWal ()
{
if (!conn_) return;
int log = 0, ckpt = 0;
int ret = sqlite3_wal_checkpoint_v2 (
conn_, nullptr, SQLITE_CHECKPOINT_PASSIVE, &log, &ckpt);
if (ret != SQLITE_OK)
{
WriteLog ((ret == SQLITE_LOCKED) ? lsTRACE : lsWARNING, WALCheckpointer)
<< "WAL(" << sqlite3_db_filename (conn_, "main") << "): error "
<< ret;
}
else
WriteLog (lsTRACE, WALCheckpointer)
<< "WAL(" << sqlite3_db_filename (conn_, "main")
<< "): frames=" << log << ", written=" << ckpt;
{
ScopedLockType sl (mutex_);
running_ = false;
}
}
}

View File

@@ -28,13 +28,37 @@
This module requires the @ref beast_sqlite external module.
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning( \
disable : 4355) // 'this' : used in base member initializer list
#endif
#include <ripple/basics/Log.h>
#include <ripple/core/JobQueue.h>
#include <beast/threads/Thread.h>
#define SOCI_USE_BOOST
#include <core/soci.h>
// #include <core/unsigned-types.h>
#include <string>
#include <cstdint>
#include <vector>
namespace sqlite_api {
class sqlite3;
}
namespace ripple {
template <class T, class C>
T rangeCheckedCast (C c)
{
if ((c > std::numeric_limits<T>::max ()) ||
(!std::numeric_limits<T>::is_signed && c < 0) ||
(std::numeric_limits<T>::is_signed &&
std::numeric_limits<C>::is_signed &&
c < std::numeric_limits<T>::lowest ()))
{
WriteLog (lsERROR, RangeCheckedCast)
<< "Range error. Min: " << std::numeric_limits<T>::lowest ()
<< " Max: " << std::numeric_limits<T>::max () << " Got: " << c;
}
return static_cast<T>(c);
}
}
namespace ripple {
class BasicConfig;
@@ -44,7 +68,7 @@ class BasicConfig;
* parsing the config parameters. If a client want to open a session immediately,
* use the free function "open" below.
*/
class SociConfig final
class SociConfig
{
std::string connectionString_;
soci::backend_factory const& backendFactory_;
@@ -70,10 +94,57 @@ void open(soci::session& s,
BasicConfig const& config,
std::string const& dbName);
/**
* Open a soci session.
*
* @param s Session to open.
* @param beName Backend name.
* @param connectionString Connection string to forward to soci::open.
* see the soci::open documentation for how to use this.
*
*/
void open(soci::session& s,
std::string const& beName,
std::string const& connectionString);
size_t getKBUsedAll (soci::session& s);
size_t getKBUsedDB (soci::session& s);
void convert(soci::blob& from, std::vector<std::uint8_t>& to);
void convert(soci::blob& from, std::string& to);
void convert(std::vector<std::uint8_t> const& from, soci::blob& to);
/** Run a thread to checkpoint the write ahead log (wal) for
the given soci::session every 1000 pages. This is only implemented
for sqlite databases.
Note: According to: https://www.sqlite.org/wal.html#ckpt this
is the default behavior of sqlite. We may be able to remove this
class.
*/
class WALCheckpointer
:private beast::Thread
{
friend int SqliteWALHook (void* s, sqlite_api::sqlite3*,
const char* dbName, int walSize);
public:
WALCheckpointer (std::shared_ptr<soci::session> const& s,
JobQueue* q);
~WALCheckpointer ();
private:
void doHook (const char* db, int walSize);
void setupCheckpointing (JobQueue*);
void run ();
void runWal ();
std::shared_ptr<soci::session> session_;
sqlite_api::sqlite3* conn_ = nullptr;
using LockType = std::mutex;
using ScopedLockType = std::lock_guard<LockType>;
LockType mutex_;
JobQueue* q_ = nullptr;
bool running_ = false;
};
}
#if _MSC_VER
#pragma warning(pop)
#endif
#endif

View File

@@ -24,22 +24,22 @@
namespace ripple {
SqliteStatement::SqliteStatement (SqliteDatabase* db, const char* sql, bool aux)
SqliteStatement::SqliteStatement (SqliteDatabase* db, const char* sql)
{
assert (db);
sqlite3* conn = aux ? db->getAuxConnection () : db->peekConnection ();
sqlite3* conn = db->peekConnection ();
int j = sqlite3_prepare_v2 (conn, sql, strlen (sql) + 1, &statement, nullptr);
if (j != SQLITE_OK)
throw j;
}
SqliteStatement::SqliteStatement (SqliteDatabase* db, std::string const& sql, bool aux)
SqliteStatement::SqliteStatement (SqliteDatabase* db, std::string const& sql)
{
assert (db);
sqlite3* conn = aux ? db->getAuxConnection () : db->peekConnection ();
sqlite3* conn = db->peekConnection ();
int j = sqlite3_prepare_v2 (conn, sql.c_str (), sql.size () + 1, &statement, nullptr);
if (j != SQLITE_OK)
@@ -62,7 +62,6 @@ SqliteDatabase::SqliteDatabase (const char* host)
startThread ();
mConnection = nullptr;
mAuxConnection = nullptr;
mCurrentStmt = nullptr;
}
@@ -85,38 +84,10 @@ void SqliteDatabase::connect ()
}
}
sqlite3* SqliteDatabase::getAuxConnection ()
{
ScopedLockType sl (m_walMutex);
if (mAuxConnection == nullptr)
{
int rc = sqlite3_open_v2 (mHost.c_str (), &mAuxConnection,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX, nullptr);
if (rc)
{
WriteLog (lsFATAL, SqliteDatabase) << "Can't aux open " << mHost << " " << rc;
assert ((rc != SQLITE_BUSY) && (rc != SQLITE_LOCKED));
if (mAuxConnection != nullptr)
{
sqlite3_close (mConnection);
mAuxConnection = nullptr;
}
}
}
return mAuxConnection;
}
void SqliteDatabase::disconnect ()
{
sqlite3_finalize (mCurrentStmt);
sqlite3_close (mConnection);
if (mAuxConnection != nullptr)
sqlite3_close (mAuxConnection);
}
// returns true if the query went ok

View File

@@ -68,7 +68,6 @@ public:
{
return mConnection;
}
sqlite3* getAuxConnection ();
virtual bool setupCheckpointing (JobQueue*);
virtual SqliteDatabase* getSqliteDB ()
{
@@ -81,6 +80,7 @@ public:
int getKBUsedAll ();
private:
void run ();
void runWal ();
@@ -89,8 +89,6 @@ private:
LockType m_walMutex;
sqlite3* mConnection;
// VFALCO TODO Why do we need an "aux" connection? Should just use a second SqliteDatabase object.
sqlite3* mAuxConnection;
sqlite3_stmt* mCurrentStmt;
bool mMoreRows;
@@ -110,11 +108,8 @@ protected:
sqlite3_stmt* statement;
public:
// VFALCO TODO This is quite a convoluted interface. A mysterious "aux" connection?
// Why not just have two SqliteDatabase objects?
//
SqliteStatement (SqliteDatabase* db, const char* statement, bool aux = false);
SqliteStatement (SqliteDatabase* db, std::string const& statement, bool aux = false);
SqliteStatement (SqliteDatabase* db, const char* statement);
SqliteStatement (SqliteDatabase* db, std::string const& statement);
~SqliteStatement ();
sqlite3_stmt* peekStatement ();

View File

@@ -39,20 +39,6 @@ private:
config.legacy ("database_path", value);
}
static void setupPostgresqlConfig (BasicConfig& config,
std::string const& host,
std::string const& user,
std::string const& port)
{
config.overwrite ("sqdb", "backend", "postgresql");
if (!host.empty ())
config.overwrite ("sqdb", "host", host);
if (!user.empty ())
config.overwrite ("sqdb", "user", user);
if (!port.empty ())
config.overwrite ("sqdb", "port", port);
}
static void cleanupDatabaseDir (boost::filesystem::path const& dbPath)
{
using namespace boost::filesystem;
@@ -109,7 +95,7 @@ public:
testcase ("sqliteFileNames");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
std::vector<std::pair<const char*, const char*>> const d (
std::vector<std::pair<std::string, std::string>> const d (
{{"peerfinder", ".sqlite"},
{"state", ".db"},
{"random", ".db"},
@@ -119,7 +105,7 @@ public:
{
SociConfig sc (c, i.first);
expect (boost::ends_with (sc.connectionString (),
std::string (i.first) + i.second));
i.first + i.second));
}
}
void testSQLiteSession ()
@@ -181,10 +167,139 @@ public:
remove (dbPath);
}
}
void testSQLiteSelect ()
{
testcase ("select");
BasicConfig c;
setupSQLiteConfig (c, getDatabasePath ());
SociConfig sc (c, "SociTestDB");
std::vector<std::uint64_t> const ubid (
{(std::uint64_t)std::numeric_limits<std::int64_t>::max (), 20, 30});
std::vector<std::int64_t> const bid ({-10, -20, -30});
std::vector<std::uint32_t> const uid (
{std::numeric_limits<std::uint32_t>::max (), 2, 3});
std::vector<std::int32_t> const id ({-1, -2, -3});
{
soci::session s;
sc.open (s);
s << "DROP TABLE IF EXISTS STT;";
s << "CREATE TABLE STT ("
" I INTEGER,"
" UI INTEGER UNSIGNED,"
" BI BIGINT,"
" UBI BIGINT UNSIGNED"
");";
s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
"(:id, :idu, :bid, :bidu);",
soci::use (id), soci::use (uid), soci::use (bid),
soci::use (ubid);
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
s << "SELECT I, UI, BI, UBI from STT", soci::into (ig),
soci::into (uig), soci::into (big), soci::into (ubig);
expect (ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
boost::optional<std::int32_t> ig;
boost::optional<std::uint32_t> uig;
boost::optional<std::int64_t> big;
boost::optional<std::uint64_t> ubig;
s << "SELECT I, UI, BI, UBI from STT", soci::into (ig),
soci::into (uig), soci::into (big), soci::into (ubig);
expect (*ig == id[0] && *uig == uid[0] && *big == bid[0] &&
*ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
// There are too many issues when working with soci::row and boost::tuple. DO NOT USE
// soci row! I had a set of workarounds to make soci row less error prone, I'm keeping
// these tests in case I try to add soci::row and boost::tuple back into soci.
#if 0
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
soci::row r;
s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
ig = r.get<std::int32_t>(0);
uig = r.get<std::uint32_t>(1);
big = r.get<std::int64_t>(2);
ubig = r.get<std::uint64_t>(3);
expect (ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
std::int32_t ig = 0;
std::uint32_t uig = 0;
std::int64_t big = 0;
std::uint64_t ubig = 0;
soci::row r;
s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
ig = r.get<std::int32_t>("I");
uig = r.get<std::uint32_t>("UI");
big = r.get<std::int64_t>("BI");
ubig = r.get<std::uint64_t>("UBI");
expect (ig == id[0] && uig == uid[0] && big == bid[0] &&
ubig == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
try
{
boost::tuple<std::int32_t,
std::uint32_t,
std::int64_t,
std::uint64_t> d;
s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
expect (get<0>(d) == id[0] && get<1>(d) == uid[0] &&
get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
}
catch (std::exception&)
{
fail ();
}
#endif
}
{
using namespace boost::filesystem;
// Remove the database
path dbPath (sc.connectionString ());
if (is_regular_file (dbPath))
remove (dbPath);
}
}
void testSQLite ()
{
testSQLiteFileNames ();
testSQLiteSession ();
testSQLiteSelect ();
}
void run ()
{

View File

@@ -26,7 +26,7 @@
#include <ripple/app/ledger/LedgerToJson.h>
#include <ripple/app/ledger/OrderBookDB.h>
#include <ripple/app/data/DatabaseCon.h>
#include <ripple/app/data/SqliteDatabase.h>
#include <ripple/app/data/SociDB.h>
#include <ripple/app/main/Application.h>
#include <ripple/app/misc/IHashRouter.h>
#include <ripple/app/misc/NetworkOPs.h>
@@ -44,6 +44,7 @@
#include <ripple/protocol/HashPrefix.h>
#include <beast/module/core/text/LexicalCast.h>
#include <beast/unit_test/suite.h>
#include <boost/optional.hpp>
namespace ripple {
@@ -694,18 +695,17 @@ bool Ledger::saveValidatedLedger (bool current)
}
{
auto sl (getApp().getLedgerDB ().lock ());
getApp().getLedgerDB ().getDB ()->executeSQL (
boost::str (deleteLedger % mLedgerSeq));
auto db = getApp().getLedgerDB ().checkoutDb();
*db << boost::str (deleteLedger % mLedgerSeq);
}
{
auto db = getApp().getTxnDB ().getDB ();
auto dbLock (getApp().getTxnDB ().lock ());
db->executeSQL ("BEGIN TRANSACTION;");
auto db = getApp().getTxnDB ().checkoutDb ();
db->executeSQL (boost::str (deleteTrans1 % getLedgerSeq ()));
db->executeSQL (boost::str (deleteTrans2 % getLedgerSeq ()));
soci::transaction tr(*db);
*db << boost::str (deleteTrans1 % getLedgerSeq ());
*db << boost::str (deleteTrans2 % getLedgerSeq ());
std::string const ledgerSeq (std::to_string (getLedgerSeq ()));
@@ -719,7 +719,7 @@ bool Ledger::saveValidatedLedger (bool current)
std::string const txnId (to_string (transactionID));
std::string const txnSeq (std::to_string (vt.second->getTxnSeq ()));
db->executeSQL (boost::str (deleteAcctTrans % transactionID));
*db << boost::str (deleteAcctTrans % transactionID);
auto const& accts = vt.second->getAffected ();
@@ -758,30 +758,31 @@ bool Ledger::saveValidatedLedger (bool current)
{
WriteLog (lsTRACE, Ledger) << "ActTx: " << sql;
}
db->executeSQL (sql);
*db << sql;
}
else
WriteLog (lsWARNING, Ledger)
<< "Transaction in ledger " << mLedgerSeq
<< " affects no accounts";
db->executeSQL (
STTx::getMetaSQLInsertReplaceHeader () +
*db <<
(STTx::getMetaSQLInsertReplaceHeader () +
vt.second->getTxn ()->getMetaSQL (
getLedgerSeq (), vt.second->getEscMeta ()) + ";");
}
db->executeSQL ("COMMIT TRANSACTION;");
tr.commit ();
}
{
auto sl (getApp().getLedgerDB ().lock ());
auto db (getApp().getLedgerDB ().checkoutDb ());
// TODO(tom): ARG!
getApp().getLedgerDB ().getDB ()->executeSQL (boost::str (addLedger %
to_string (getHash ()) % mLedgerSeq % to_string (mParentHash) %
beast::lexicalCastThrow <std::string> (mTotCoins) % mCloseTime %
mParentCloseTime % mCloseResolution % mCloseFlags %
to_string (mAccountHash) % to_string (mTransHash)));
*db << boost::str (addLedger %
to_string (getHash ()) % mLedgerSeq % to_string (mParentHash) %
beast::lexicalCastThrow <std::string> (mTotCoins) % mCloseTime %
mParentCloseTime % mCloseResolution % mCloseFlags %
to_string (mAccountHash) % to_string (mTransHash));
}
{
@@ -793,207 +794,127 @@ bool Ledger::saveValidatedLedger (bool current)
return true;
}
#ifndef NO_SQLITE3_PREPARE
Ledger::pointer Ledger::loadByIndex (std::uint32_t ledgerIndex)
/*
* Load a ledger from the database.
*
* @param sqlSuffix: Additional string to append to the sql query.
* (typically a where clause).
* @return The ledger, ledger sequence, and ledger hash.
*/
std::tuple<Ledger::pointer, std::uint32_t, uint256>
loadHelper(std::string const& sqlSuffix)
{
Ledger::pointer ledger;
uint256 ledgerHash;
std::uint32_t ledgerSeq{0};
auto db = getApp ().getLedgerDB ().checkoutDb ();
boost::optional<std::string> sLedgerHash, sPrevHash, sAccountHash,
sTransHash;
boost::optional<std::uint64_t> totCoins, closingTime, prevClosingTime,
closeResolution, closeFlags, ledgerSeq64;
std::string const sql =
"SELECT "
"LedgerHash, PrevHash, AccountSetHash, TransSetHash, "
"TotalCoins,"
"ClosingTime, PrevClosingTime, CloseTimeRes, CloseFlags,"
"LedgerSeq from Ledgers " +
sqlSuffix + ";";
*db << sql,
soci::into(sLedgerHash),
soci::into(sPrevHash),
soci::into(sAccountHash),
soci::into(sTransHash),
soci::into(totCoins),
soci::into(closingTime),
soci::into(prevClosingTime),
soci::into(closeResolution),
soci::into(closeFlags),
soci::into(ledgerSeq64);
if (!db->got_data ())
{
auto db = getApp().getLedgerDB ().getDB ();
auto sl (getApp().getLedgerDB ().lock ());
SqliteStatement pSt (
db->getSqliteDB (), "SELECT "
"LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins,"
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
" from Ledgers WHERE LedgerSeq = ?;");
pSt.bind (1, ledgerIndex);
ledger = getSQL1 (&pSt);
std::stringstream s;
WriteLog (lsINFO, Ledger) << "Ledger not found: " << sqlSuffix;
return std::make_tuple (Ledger::pointer (), ledgerSeq, ledgerHash);
}
if (ledger)
{
Ledger::getSQL2 (ledger);
ledger->setFull ();
}
ledgerSeq =
rangeCheckedCast<std::uint32_t>(ledgerSeq64.value_or (0));
return ledger;
}
uint256 prevHash, accountHash, transHash;
ledgerHash.SetHexExact (sLedgerHash.value_or(""));
prevHash.SetHexExact (sPrevHash.value_or(""));
accountHash.SetHexExact (sAccountHash.value_or(""));
transHash.SetHexExact (sTransHash.value_or(""));
Ledger::pointer Ledger::loadByHash (uint256 const& ledgerHash)
{
Ledger::pointer ledger;
{
auto db = getApp().getLedgerDB ().getDB ();
auto sl (getApp().getLedgerDB ().lock ());
SqliteStatement pSt (
db->getSqliteDB (), "SELECT "
"LedgerHash,PrevHash,AccountSetHash,TransSetHash,TotalCoins,"
"ClosingTime,PrevClosingTime,CloseTimeRes,CloseFlags,LedgerSeq"
" from Ledgers WHERE LedgerHash = ?;");
pSt.bind (1, to_string (ledgerHash));
ledger = getSQL1 (&pSt);
}
if (ledger)
{
assert (ledger->getHash () == ledgerHash);
Ledger::getSQL2 (ledger);
ledger->setFull ();
}
return ledger;
}
#else
Ledger::pointer Ledger::loadByIndex (std::uint32_t ledgerIndex)
{
// This is a low-level function with no caching.
std::string sql = "SELECT * from Ledgers WHERE LedgerSeq='";
sql.append (beast::lexicalCastThrow <std::string> (ledgerIndex));
sql.append ("';");
return getSQL (sql);
}
Ledger::pointer Ledger::loadByHash (uint256 const& ledgerHash)
{
// This is a low-level function with no caching and only gets accepted
// ledgers.
std::string sql = "SELECT * from Ledgers WHERE LedgerHash='";
sql.append (to_string (ledgerHash));
sql.append ("';");
return getSQL (sql);
}
#endif
Ledger::pointer Ledger::getSQL (std::string const& sql)
{
// only used with sqlite3 prepared statements not used
uint256 ledgerHash, prevHash, accountHash, transHash;
std::uint64_t totCoins;
std::uint32_t closingTime, prevClosingTime, ledgerSeq;
int closeResolution;
unsigned closeFlags;
std::string hash;
{
auto db = getApp().getLedgerDB ().getDB ();
auto sl (getApp().getLedgerDB ().lock ());
if (!db->executeSQL (sql) || !db->startIterRows ())
return Ledger::pointer ();
db->getStr ("LedgerHash", hash);
ledgerHash.SetHexExact (hash);
db->getStr ("PrevHash", hash);
prevHash.SetHexExact (hash);
db->getStr ("AccountSetHash", hash);
accountHash.SetHexExact (hash);
db->getStr ("TransSetHash", hash);
transHash.SetHexExact (hash);
totCoins = db->getBigInt ("TotalCoins");
closingTime = db->getBigInt ("ClosingTime");
prevClosingTime = db->getBigInt ("PrevClosingTime");
closeResolution = db->getBigInt ("CloseTimeRes");
closeFlags = db->getBigInt ("CloseFlags");
ledgerSeq = db->getBigInt ("LedgerSeq");
db->endIterRows ();
}
// CAUTION: code below appears in two places
bool loaded;
Ledger::pointer ret (new Ledger (
prevHash, transHash, accountHash, totCoins, closingTime,
prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded));
bool loaded = false;
ledger = std::make_shared<Ledger>(prevHash,
transHash,
accountHash,
totCoins.value_or(0),
closingTime.value_or(0),
prevClosingTime.value_or(0),
closeFlags.value_or(0),
closeResolution.value_or(0),
ledgerSeq,
loaded);
if (!loaded)
return Ledger::pointer ();
return std::make_tuple (Ledger::pointer (), ledgerSeq, ledgerHash);
ret->setClosed ();
if (getApp().getOPs ().haveLedger (ledgerSeq))
{
ret->setAccepted ();
ret->setValidated ();
}
if (ret->getHash () != ledgerHash)
{
if (ShouldLog (lsERROR, Ledger))
{
WriteLog (lsERROR, Ledger) << "Failed on ledger";
Json::Value p;
addJson (p, {*ret, LedgerFill::full});
WriteLog (lsERROR, Ledger) << p;
}
assert (false);
return Ledger::pointer ();
}
WriteLog (lsTRACE, Ledger) << "Loaded ledger: " << ledgerHash;
return ret;
return std::make_tuple (ledger, ledgerSeq, ledgerHash);
}
Ledger::pointer Ledger::getSQL1 (SqliteStatement* stmt)
void finishLoadByIndexOrHash(Ledger::pointer& ledger)
{
int iRet = stmt->step ();
if (!ledger)
return;
if (stmt->isDone (iRet))
return Ledger::pointer ();
ledger->setClosed ();
ledger->setImmutable ();
if (!stmt->isRow (iRet))
{
WriteLog (lsINFO, Ledger)
<< "Ledger not found: " << iRet
<< " = " << stmt->getError (iRet);
return Ledger::pointer ();
}
uint256 ledgerHash, prevHash, accountHash, transHash;
std::uint64_t totCoins;
std::uint32_t closingTime, prevClosingTime, ledgerSeq;
int closeResolution;
unsigned closeFlags;
ledgerHash.SetHexExact (stmt->peekString (0));
prevHash.SetHexExact (stmt->peekString (1));
accountHash.SetHexExact (stmt->peekString (2));
transHash.SetHexExact (stmt->peekString (3));
totCoins = stmt->getInt64 (4);
closingTime = stmt->getUInt32 (5);
prevClosingTime = stmt->getUInt32 (6);
closeResolution = stmt->getUInt32 (7);
closeFlags = stmt->getUInt32 (8);
ledgerSeq = stmt->getUInt32 (9);
// CAUTION: code below appears in two places
bool loaded;
Ledger::pointer ret (new Ledger (
prevHash, transHash, accountHash, totCoins, closingTime,
prevClosingTime, closeFlags, closeResolution, ledgerSeq, loaded));
if (!loaded)
return Ledger::pointer ();
return ret;
}
void Ledger::getSQL2 (Ledger::ref ret)
{
ret->setClosed ();
ret->setImmutable ();
if (getApp().getOPs ().haveLedger (ret->getLedgerSeq ()))
ret->setAccepted ();
if (getApp ().getOPs ().haveLedger (ledger->getLedgerSeq ()))
ledger->setAccepted ();
WriteLog (lsTRACE, Ledger)
<< "Loaded ledger: " << to_string (ret->getHash ());
<< "Loaded ledger: " << to_string (ledger->getHash ());
ledger->setFull ();
}
Ledger::pointer Ledger::loadByIndex (std::uint32_t ledgerIndex)
{
Ledger::pointer ledger;
{
std::ostringstream s;
s << "WHERE LedgerSeq = " << ledgerIndex;
std::tie (ledger, std::ignore, std::ignore) =
loadHelper (s.str ());
}
finishLoadByIndexOrHash (ledger);
return ledger;
}
Ledger::pointer Ledger::loadByHash (uint256 const& ledgerHash)
{
Ledger::pointer ledger;
{
std::ostringstream s;
s << "WHERE LedgerHash = '" << ledgerHash << "'";
std::tie (ledger, std::ignore, std::ignore) =
loadHelper (s.str ());
}
finishLoadByIndexOrHash (ledger);
assert (!ledger || ledger->getHash () == ledgerHash);
return ledger;
}
uint256 Ledger::getHashByIndex (std::uint32_t ledgerIndex)
@@ -1007,14 +928,18 @@ uint256 Ledger::getHashByIndex (std::uint32_t ledgerIndex)
std::string hash;
{
auto db = getApp().getLedgerDB ().getDB ();
auto sl (getApp().getLedgerDB ().lock ());
auto db = getApp().getLedgerDB ().checkoutDb ();
if (!db->executeSQL (sql) || !db->startIterRows ())
boost::optional<std::string> lh;
*db << sql,
soci::into (lh);
if (!db->got_data () || !lh)
return ret;
db->getStr ("LedgerHash", hash);
db->endIterRows ();
hash = *lh;
if (hash.empty ())
return ret;
}
ret.SetHexExact (hash);
@@ -1024,66 +949,26 @@ uint256 Ledger::getHashByIndex (std::uint32_t ledgerIndex)
bool Ledger::getHashesByIndex (
std::uint32_t ledgerIndex, uint256& ledgerHash, uint256& parentHash)
{
#ifndef NO_SQLITE3_PREPARE
auto db = getApp().getLedgerDB ().checkoutDb ();
auto& con = getApp().getLedgerDB ();
auto sl (con.lock ());
boost::optional <std::string> lhO, phO;
*db << "SELECT LedgerHash,PrevHash FROM Ledgers "
"INDEXED BY SeqLedger Where LedgerSeq = :ls;",
soci::into (lhO),
soci::into (phO),
soci::use (ledgerIndex);
SqliteStatement pSt (con.getDB ()->getSqliteDB (),
"SELECT LedgerHash,PrevHash FROM Ledgers "
"INDEXED BY SeqLedger Where LedgerSeq = ?;");
pSt.bind (1, ledgerIndex);
int ret = pSt.step ();
if (pSt.isDone (ret))
if (!lhO || !phO)
{
WriteLog (lsTRACE, Ledger) << "Don't have ledger " << ledgerIndex;
return false;
}
if (!pSt.isRow (ret))
{
assert (false);
WriteLog (lsFATAL, Ledger) << "Unexpected statement result " << ret;
return false;
}
ledgerHash.SetHexExact (pSt.peekString (0));
parentHash.SetHexExact (pSt.peekString (1));
ledgerHash.SetHexExact (*lhO);
parentHash.SetHexExact (*phO);
return true;
#else
std::string sql =
"SELECT LedgerHash,PrevHash FROM Ledgers WHERE LedgerSeq='";
sql.append (beast::lexicalCastThrow <std::string> (ledgerIndex));
sql.append ("';");
std::string hash, prevHash;
{
auto db = getApp().getLedgerDB ().getDB ();
auto sl (getApp().getLedgerDB ().lock ());
if (!db->executeSQL (sql) || !db->startIterRows ())
return false;
db->getStr ("LedgerHash", hash);
db->getStr ("PrevHash", prevHash);
db->endIterRows ();
}
ledgerHash.SetHexExact (hash);
parentHash.SetHexExact (prevHash);
assert (ledgerHash.isNonZero () &&
(ledgerIndex == 0 || parentHash.isNonZero ());
return true;
#endif
}
std::map< std::uint32_t, std::pair<uint256, uint256> >
@@ -1098,16 +983,29 @@ Ledger::getHashesByIndex (std::uint32_t minSeq, std::uint32_t maxSeq)
sql.append (beast::lexicalCastThrow <std::string> (maxSeq));
sql.append (";");
auto& con = getApp().getLedgerDB ();
auto sl (con.lock ());
auto db = getApp().getLedgerDB ().checkoutDb ();
SqliteStatement pSt (con.getDB ()->getSqliteDB (), sql);
std::uint64_t ls;
std::string lh;
boost::optional<std::string> ph;
soci::statement st =
(db->prepare << sql,
soci::into (ls),
soci::into (lh),
soci::into (ph));
while (pSt.isRow (pSt.step ()))
st.execute ();
while (st.fetch ())
{
std::pair<uint256, uint256>& hashes = ret[pSt.getUInt32 (0)];
hashes.first.SetHexExact (pSt.peekString (1));
hashes.second.SetHexExact (pSt.peekString (2));
std::pair<uint256, uint256>& hashes =
ret[rangeCheckedCast<std::uint32_t>(ls)];
hashes.first.SetHexExact (lh);
hashes.second.SetHexExact (ph.value_or (""));
if (!ph)
{
WriteLog (lsWARNING, Ledger)
<< "Null prev hash for ledger seq: " << ls;
}
}
return ret;
@@ -1117,7 +1015,39 @@ Ledger::pointer Ledger::getLastFullLedger ()
{
try
{
return getSQL ("SELECT * from Ledgers order by LedgerSeq desc limit 1;");
Ledger::pointer ledger;
std::uint32_t ledgerSeq;
uint256 ledgerHash;
std::tie (ledger, ledgerSeq, ledgerHash) =
loadHelper ("order by LedgerSeq desc limit 1");
if (!ledger)
return ledger;
ledger->setClosed ();
if (getApp().getOPs ().haveLedger (ledgerSeq))
{
ledger->setAccepted ();
ledger->setValidated ();
}
if (ledger->getHash () != ledgerHash)
{
if (ShouldLog (lsERROR, Ledger))
{
WriteLog (lsERROR, Ledger) << "Failed on ledger";
Json::Value p;
addJson (p, {*ledger, LedgerFill::full});
WriteLog (lsERROR, Ledger) << p;
}
assert (false);
return Ledger::pointer ();
}
WriteLog (lsTRACE, Ledger) << "Loaded ledger: " << ledgerHash;
return ledger;
}
catch (SHAMapMissingNode& sn)
{

View File

@@ -137,9 +137,6 @@ public:
~Ledger ();
static Ledger::pointer getSQL (std::string const& sqlStatement);
static Ledger::pointer getSQL1 (SqliteStatement*);
static void getSQL2 (Ledger::ref);
static Ledger::pointer getLastFullLedger ();
static std::uint32_t roundCloseTime (
std::uint32_t closeTime, std::uint32_t closeResolution);

View File

@@ -713,13 +713,16 @@ public:
exitWithCode(3);
}
getApp().getLedgerDB ().getDB ()->executeSQL (boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(getConfig ().getSize (siLgrDBCache) * 1024)));
getApp().getTxnDB ().getDB ()->executeSQL (boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(getConfig ().getSize (siTxnDBCache) * 1024)));
getApp ().getLedgerDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(getConfig ().getSize (siLgrDBCache) * 1024));
mTxnDB->getDB ()->setupCheckpointing (m_jobQueue.get());
mLedgerDB->getDB ()->setupCheckpointing (m_jobQueue.get());
getApp().getTxnDB ().getSession ()
<< boost::str (boost::format ("PRAGMA cache_size=-%d;") %
(getConfig ().getSize (siTxnDBCache) * 1024));
mTxnDB->setupCheckpointing (m_jobQueue.get());
mLedgerDB->setupCheckpointing (m_jobQueue.get());
if (!getConfig ().RUN_STANDALONE)
updateTables ();
@@ -1350,15 +1353,19 @@ bool serverOkay (std::string& reason)
static std::vector<std::string> getSchema (DatabaseCon& dbc, std::string const& dbName)
{
std::vector<std::string> schema;
schema.reserve(32);
std::string sql = "SELECT sql FROM sqlite_master WHERE tbl_name='";
sql += dbName;
sql += "';";
SQL_FOREACH (dbc.getDB (), sql)
std::string r;
soci::statement st = (dbc.getSession ().prepare << sql,
soci::into(r));
st.execute ();
while (st.fetch ())
{
dbc.getDB ()->getStr ("sql", sql);
schema.push_back (sql);
schema.emplace_back (r);
}
return schema;
@@ -1384,7 +1391,7 @@ static void addTxnSeqField ()
WriteLog (lsWARNING, Application) << "Transaction sequence field is missing";
auto db = getApp().getTxnDB ().getDB ();
auto& session = getApp().getTxnDB ().getSession ();
std::vector< std::pair<uint256, int> > txIDs;
txIDs.reserve (300000);
@@ -1392,32 +1399,37 @@ static void addTxnSeqField ()
WriteLog (lsINFO, Application) << "Parsing transactions";
int i = 0;
uint256 transID;
SQL_FOREACH (db, "SELECT TransID,TxnMeta FROM Transactions;")
boost::optional<std::string> strTransId;
soci::blob sociTxnMetaBlob(session);
soci::indicator tmi;
Blob txnMeta;
soci::statement st =
(session.prepare <<
"SELECT TransID, TxnMeta FROM Transactions;",
soci::into(strTransId),
soci::into(sociTxnMetaBlob, tmi));
st.execute ();
while (st.fetch ())
{
Blob rawMeta;
int metaSize = 2048;
rawMeta.resize (metaSize);
metaSize = db->getBinary ("TxnMeta", &*rawMeta.begin (), rawMeta.size ());
if (soci::i_ok == tmi)
convert (sociTxnMetaBlob, txnMeta);
else
txnMeta.clear ();
if (metaSize > static_cast<int> (rawMeta.size ()))
{
rawMeta.resize (metaSize);
db->getBinary ("TxnMeta", &*rawMeta.begin (), rawMeta.size ());
}
else rawMeta.resize (metaSize);
std::string tid;
db->getStr ("TransID", tid);
std::string tid = strTransId.value_or("");
transID.SetHex (tid, true);
if (rawMeta.size () == 0)
if (txnMeta.size () == 0)
{
txIDs.push_back (std::make_pair (transID, -1));
WriteLog (lsINFO, Application) << "No metadata for " << transID;
}
else
{
TransactionMetaSet m (transID, 0, rawMeta);
TransactionMetaSet m (transID, 0, txnMeta);
txIDs.push_back (std::make_pair (transID, m.getIndex ()));
}
@@ -1429,19 +1441,19 @@ static void addTxnSeqField ()
WriteLog (lsINFO, Application) << "All " << i << " transactions read";
db->executeSQL ("BEGIN TRANSACTION;");
soci::transaction tr(session);
WriteLog (lsINFO, Application) << "Dropping old index";
db->executeSQL ("DROP INDEX AcctTxIndex;");
session << "DROP INDEX AcctTxIndex;";
WriteLog (lsINFO, Application) << "Altering table";
db->executeSQL ("ALTER TABLE AccountTransactions ADD COLUMN TxnSeq INTEGER;");
session << "ALTER TABLE AccountTransactions ADD COLUMN TxnSeq INTEGER;";
boost::format fmt ("UPDATE AccountTransactions SET TxnSeq = %d WHERE TransID = '%s';");
i = 0;
for (auto& t : txIDs)
{
db->executeSQL (boost::str (fmt % t.second % to_string (t.first)));
session << boost::str (fmt % t.second % to_string (t.first));
if ((++i % 1000) == 0)
{
@@ -1450,8 +1462,9 @@ static void addTxnSeqField ()
}
WriteLog (lsINFO, Application) << "Building new index";
db->executeSQL ("CREATE INDEX AcctTxIndex ON AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);");
db->executeSQL ("END TRANSACTION;");
session << "CREATE INDEX AcctTxIndex ON AccountTransactions(Account, LedgerSeq, TxnSeq, TransID);";
tr.commit ();
}
void ApplicationImp::updateTables ()

View File

@@ -26,6 +26,7 @@
#include <ripple/basics/StringUtilities.h>
#include <ripple/basics/make_SSLContext.h>
#include <ripple/core/Config.h>
#include <boost/optional.hpp>
#include <iostream>
namespace ripple {
@@ -53,21 +54,21 @@ void LocalCredentials::start ()
// Retrieve network identity.
bool LocalCredentials::nodeIdentityLoad ()
{
auto db = getApp().getWalletDB ().getDB ();
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
bool bSuccess = false;
if (db->executeSQL ("SELECT * FROM NodeIdentity;") && db->startIterRows ())
boost::optional<std::string> pubKO, priKO;
soci::statement st = (db->prepare <<
"SELECT PublicKey, PrivateKey "
"FROM NodeIdentity;",
soci::into(pubKO),
soci::into(priKO));
st.execute ();
while (st.fetch ())
{
std::string strPublicKey, strPrivateKey;
mNodePublicKey.setNodePublic (pubKO.value_or(""));
mNodePrivateKey.setNodePrivate (priKO.value_or(""));
db->getStr ("PublicKey", strPublicKey);
db->getStr ("PrivateKey", strPrivateKey);
mNodePublicKey.setNodePublic (strPublicKey);
mNodePrivateKey.setNodePrivate (strPrivateKey);
db->endIterRows ();
bSuccess = true;
}
@@ -101,15 +102,13 @@ bool LocalCredentials::nodeIdentityCreate ()
//
// Store the node information
//
auto db = getApp().getWalletDB ().getDB ();
auto db = getApp().getWalletDB ().checkoutDb ();
auto sl (getApp().getWalletDB ().lock ());
db->executeSQL (str (boost::format ("INSERT INTO NodeIdentity (PublicKey,PrivateKey,Dh512,Dh1024) VALUES ('%s','%s',%s,%s);")
*db << str (boost::format ("INSERT INTO NodeIdentity (PublicKey,PrivateKey,Dh512,Dh1024) VALUES ('%s','%s',%s,%s);")
% naNodePublic.humanNodePublic ()
% naNodePrivate.humanNodePrivate ()
% sqlEscape (strDh512)
% sqlEscape (strDh1024)));
// XXX Check error result.
% sqlEscape (strDh1024));
if (!getConfig ().QUIET)
std::cerr << "NodeIdentity: Created." << std::endl;
@@ -119,30 +118,28 @@ bool LocalCredentials::nodeIdentityCreate ()
bool LocalCredentials::dataDelete (std::string const& strKey)
{
auto db = getApp().getRpcDB ().getDB ();
auto db = getApp().getRpcDB ().checkoutDb ();
auto sl (getApp().getRpcDB ().lock ());
return db->executeSQL (str (boost::format ("DELETE FROM RPCData WHERE Key=%s;")
% sqlEscape (strKey)));
*db << (str (boost::format ("DELETE FROM RPCData WHERE Key=%s;")
% sqlEscape (strKey)));
return true;
}
bool LocalCredentials::dataFetch (std::string const& strKey, std::string& strValue)
{
auto db = getApp().getRpcDB ().getDB ();
auto sl (getApp().getRpcDB ().lock ());
auto db = getApp().getRpcDB ().checkoutDb ();
bool bSuccess = false;
if (db->executeSQL (str (boost::format ("SELECT Value FROM RPCData WHERE Key=%s;")
% sqlEscape (strKey))) && db->startIterRows ())
soci::blob value (*db);
soci::indicator vi;
*db << str (boost::format ("SELECT Value FROM RPCData WHERE Key=%s;")
% sqlEscape (strKey)),
soci::into(value, vi);
if (soci::i_ok == vi)
{
Blob vucData = db->getBinary ("Value");
strValue.assign (vucData.begin (), vucData.end ());
db->endIterRows ();
convert (value, strValue);
bSuccess = true;
}
@@ -151,14 +148,12 @@ bool LocalCredentials::dataFetch (std::string const& strKey, std::string& strVal
bool LocalCredentials::dataStore (std::string const& strKey, std::string const& strValue)
{
auto db = getApp().getRpcDB ().getDB ();
auto db = getApp().getRpcDB ().checkoutDb ();
auto sl (getApp().getRpcDB ().lock ());
return (db->executeSQL (str (boost::format ("REPLACE INTO RPCData (Key, Value) VALUES (%s,%s);")
% sqlEscape (strKey)
% sqlEscape (strValue)
)));
*db << (str (boost::format ("REPLACE INTO RPCData (Key, Value) VALUES (%s,%s);")
% sqlEscape (strKey)
% sqlEscape (strValue)));
return true;
}
} // ripple

View File

@@ -163,11 +163,11 @@ static
void
setupConfigForUnitTests (Config& config)
{
config->overwrite (ConfigSection::nodeDatabase (), "type", "memory");
config->overwrite (ConfigSection::nodeDatabase (), "path", "main");
config.overwrite (ConfigSection::nodeDatabase (), "type", "memory");
config.overwrite (ConfigSection::nodeDatabase (), "path", "main");
config->deprecatedClearSection (ConfigSection::tempNodeDatabase ());
config->deprecatedClearSection (ConfigSection::importNodeDatabase ());
config.deprecatedClearSection (ConfigSection::tempNodeDatabase ());
config.deprecatedClearSection (ConfigSection::importNodeDatabase ());
config.legacy("database_path", "DummyForUnitTests");
}

View File

@@ -693,15 +693,17 @@ void AppApiFacadeImpl::setMajorityTimesFromDBToState (
query.append (to_string (amendmentHash));
query.append ("';");
auto& walletDB (getApp ().getWalletDB ());
auto sl (walletDB.lock ());
auto db (walletDB.getDB ());
auto db (getApp ().getWalletDB ().checkoutDb ());
if (db->executeSQL (query) && db->startIterRows ())
boost::optional<std::uint64_t> fm, lm;
soci::statement st = (db->prepare << query,
soci::into(fm),
soci::into(lm));
st.execute ();
while (st.fetch ())
{
toUpdate.m_firstMajority = db->getBigInt ("FirstMajority");
toUpdate.m_lastMajority = db->getBigInt ("LastMajority");
db->endIterRows ();
toUpdate.m_firstMajority = fm.value_or (0);
toUpdate.m_lastMajority = lm.value_or (0);
}
}
@@ -712,24 +714,22 @@ void AppApiFacadeImpl::setMajorityTimesFromStateToDB (
if (changedAmendments.empty ())
return;
auto& walletDB (getApp ().getWalletDB ());
auto sl (walletDB.lock ());
auto db (walletDB.getDB ());
auto db (getApp ().getWalletDB ().checkoutDb ());
db->executeSQL ("BEGIN TRANSACTION;");
soci::transaction tr(*db);
for (auto const& hash : changedAmendments)
{
AmendmentState const& fState = amendmentMap[hash];
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET FirstMajority "
"= %d WHERE Hash = '%s';") %
fState.m_firstMajority % to_string (hash)));
db->executeSQL (boost::str (boost::format (
"UPDATE Features SET LastMajority "
"= %d WHERE Hash = '%s';") %
fState.m_lastMajority % to_string (hash)));
*db << boost::str (boost::format ("UPDATE Features SET FirstMajority "
"= %d WHERE Hash = '%s';") %
fState.m_firstMajority % to_string (hash));
*db << boost::str (boost::format ("UPDATE Features SET LastMajority "
"= %d WHERE Hash = '%s';") %
fState.m_lastMajority % to_string (hash));
}
db->executeSQL ("END TRANSACTION;");
tr.commit ();
}
ValidationSet AppApiFacadeImpl::getValidations (uint256 const& hash) const

View File

@@ -60,6 +60,7 @@
#include <beast/module/core/thread/DeadlineTimer.h>
#include <beast/module/core/system/SystemStats.h>
#include <beast/cxx14/memory.h> // <memory>
#include <boost/optional.hpp>
#include <tuple>
namespace ripple {
@@ -1933,32 +1934,42 @@ NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs (
minLedger, maxLedger, descending, offset, limit, false, false, bAdmin);
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().checkoutDb ();
SQL_FOREACH (db, sql)
boost::optional<std::uint64_t> ledgerSeq;
boost::optional<std::string> status;
soci::blob sociTxnBlob (*db), sociTxnMetaBlob (*db);
soci::indicator rti, tmi;
Blob rawTxn, txnMeta;
soci::statement st =
(db->prepare << sql,
soci::into(ledgerSeq),
soci::into(status),
soci::into(sociTxnBlob, rti),
soci::into(sociTxnMetaBlob, tmi));
st.execute ();
while (st.fetch ())
{
auto txn = Transaction::transactionFromSQL (db, Validate::NO);
Serializer rawMeta;
int metaSize = 2048;
rawMeta.resize (metaSize);
metaSize = db->getBinary (
"TxnMeta", &*rawMeta.begin (), rawMeta.getLength ());
if (metaSize > rawMeta.getLength ())
{
rawMeta.resize (metaSize);
db->getBinary (
"TxnMeta", &*rawMeta.begin (), rawMeta.getLength ());
}
if (soci::i_ok == rti)
convert(sociTxnBlob, rawTxn);
else
rawMeta.resize (metaSize);
rawTxn.clear ();
if (rawMeta.getLength() == 0)
if (soci::i_ok == tmi)
convert (sociTxnMetaBlob, txnMeta);
else
txnMeta.clear ();
auto txn = Transaction::transactionFromSQL (
ledgerSeq, status, rawTxn, Validate::NO);
if (txnMeta.empty ())
{ // Work around a bug that could leave the metadata missing
auto seq = static_cast<std::uint32_t>(
db->getBigInt("LedgerSeq"));
auto const seq = rangeCheckedCast<std::uint32_t>(
ledgerSeq.value_or (0));
m_journal.warning << "Recovering ledger " << seq
<< ", txn " << txn->getID();
Ledger::pointer ledger = getLedgerBySeq(seq);
@@ -1967,7 +1978,7 @@ NetworkOPs::AccountTxs NetworkOPsImp::getAccountTxs (
}
ret.emplace_back (txn, std::make_shared<TransactionMetaSet> (
txn->getID (), txn->getLedger (), rawMeta.getData ()));
txn->getID (), txn->getLedger (), txnMeta));
}
}
@@ -1988,37 +1999,35 @@ std::vector<NetworkOPsImp::txnMetaLedgerType> NetworkOPsImp::getAccountTxsB (
bAdmin);
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().checkoutDb ();
SQL_FOREACH (db, sql)
boost::optional<std::uint64_t> ledgerSeq;
boost::optional<std::string> status;
soci::blob sociTxnBlob (*db), sociTxnMetaBlob (*db);
soci::indicator rti, tmi;
soci::statement st =
(db->prepare << sql,
soci::into(ledgerSeq),
soci::into(status),
soci::into(sociTxnBlob, rti),
soci::into(sociTxnMetaBlob, tmi));
st.execute ();
while (st.fetch ())
{
int txnSize = 2048;
Blob rawTxn (txnSize);
txnSize = db->getBinary ("RawTxn", &rawTxn[0], rawTxn.size ());
Blob rawTxn;
if (soci::i_ok == rti)
convert (sociTxnBlob, rawTxn);
Blob txnMeta;
if (soci::i_ok == tmi)
convert (sociTxnMetaBlob, txnMeta);
if (txnSize > rawTxn.size ())
{
rawTxn.resize (txnSize);
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.size ());
}
else
rawTxn.resize (txnSize);
int metaSize = 2048;
Blob rawMeta (metaSize);
metaSize = db->getBinary ("TxnMeta", &rawMeta[0], rawMeta.size ());
if (metaSize > rawMeta.size ())
{
rawMeta.resize (metaSize);
db->getBinary ("TxnMeta", &*rawMeta.begin (), rawMeta.size ());
}
else
rawMeta.resize (metaSize);
auto const seq =
rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or (0));
ret.emplace_back (
strHex (rawTxn), strHex (rawMeta), db->getInt ("LedgerSeq"));
strHex (rawTxn), strHex (txnMeta), seq);
}
}
@@ -2085,51 +2094,63 @@ NetworkOPsImp::AccountTxs NetworkOPsImp::getTxsAccount (
% (forward ? "ASC" : "DESC")
% queryLimit);
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().checkoutDb ();
SQL_FOREACH (db, sql)
boost::optional<std::uint64_t> ledgerSeq64;
boost::optional<std::int32_t> txnSeq;
boost::optional<std::string> status;
soci::blob sociTxnBlob (*db), sociTxnMetaBlob (*db);
soci::indicator rti, tmi;
Blob rawTxn, txnMeta;
soci::statement st =
(db->prepare << sql,
soci::into(ledgerSeq64),
soci::into(txnSeq),
soci::into(status),
soci::into(sociTxnBlob, rti),
soci::into(sociTxnMetaBlob, tmi));
st.execute ();
while (st.fetch ())
{
if (soci::i_ok == rti)
convert(sociTxnBlob, rawTxn);
else
rawTxn.clear ();
if (soci::i_ok == tmi)
convert (sociTxnMetaBlob, txnMeta);
else
txnMeta.clear ();
auto const ledgerSeq = rangeCheckedCast<std::uint32_t>(
ledgerSeq64.value_or (0));
if (!foundResume)
{
foundResume = (findLedger == db->getInt("LedgerSeq") &&
findSeq == db->getInt("TxnSeq"));
foundResume = (findLedger == ledgerSeq &&
findSeq == txnSeq.value_or (0));
}
else if (numberOfResults == 0)
{
token = Json::objectValue;
token[jss::ledger] = db->getInt("LedgerSeq");
token[jss::seq] = db->getInt("TxnSeq");
token[jss::ledger] = ledgerSeq;
token[jss::seq] = txnSeq.value_or (0);
break;
}
if (foundResume)
{
auto txn = Transaction::transactionFromSQL (db, Validate::NO);
auto txn = Transaction::transactionFromSQL (
ledgerSeq64, status, rawTxn, Validate::NO);
Serializer rawMeta;
int metaSize = 2048;
rawMeta.resize (metaSize);
metaSize = db->getBinary (
"TxnMeta", &*rawMeta.begin (), rawMeta.getLength ());
if (metaSize > rawMeta.getLength ())
{
rawMeta.resize (metaSize);
db->getBinary (
"TxnMeta", &*rawMeta.begin (), rawMeta.getLength ());
}
else
rawMeta.resize (metaSize);
if (rawMeta.getLength() == 0)
if (txnMeta.empty ())
{
// Work around a bug that could leave the metadata missing
auto seq = static_cast<std::uint32_t>(
db->getBigInt("LedgerSeq"));
m_journal.warning << "Recovering ledger " << seq
m_journal.warning << "Recovering ledger " << ledgerSeq
<< ", txn " << txn->getID();
Ledger::pointer ledger = getLedgerBySeq(seq);
Ledger::pointer ledger = getLedgerBySeq(ledgerSeq);
if (ledger)
ledger->pendSaveValidated(false, false);
}
@@ -2138,7 +2159,7 @@ NetworkOPsImp::AccountTxs NetworkOPsImp::getTxsAccount (
ret.emplace_back (std::move (txn),
std::make_shared<TransactionMetaSet> (
txn->getID (), txn->getLedger (), rawMeta.getData ()));
txn->getID (), txn->getLedger (), txnMeta));
}
}
}
@@ -2203,15 +2224,30 @@ NetworkOPsImp::MetaTxsList NetworkOPsImp::getTxsAccountB (
% (forward ? "ASC" : "DESC")
% queryLimit);
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().checkoutDb ();
SQL_FOREACH (db, sql)
boost::optional<std::int64_t> ledgerSeq;
boost::optional<std::int32_t> txnSeq;
boost::optional<std::string> status;
soci::blob sociTxnBlob (*db);
soci::indicator rtI;
soci::blob sociTxnMetaBlob (*db);
soci::indicator tmI;
soci::statement st = (db->prepare << sql,
soci::into (ledgerSeq),
soci::into (txnSeq),
soci::into (status),
soci::into (sociTxnBlob, rtI),
soci::into (sociTxnMetaBlob, tmI));
st.execute ();
while (st.fetch ())
{
if (!foundResume)
{
if (findLedger == db->getInt("LedgerSeq") &&
findSeq == db->getInt("TxnSeq"))
if (findLedger == ledgerSeq.value_or (0) &&
findSeq == txnSeq.value_or (0))
{
foundResume = true;
}
@@ -2219,43 +2255,25 @@ NetworkOPsImp::MetaTxsList NetworkOPsImp::getTxsAccountB (
else if (numberOfResults == 0)
{
token = Json::objectValue;
token[jss::ledger] = db->getInt("LedgerSeq");
token[jss::seq] = db->getInt("TxnSeq");
token[jss::ledger] = rangeCheckedCast<std::int32_t>(
ledgerSeq.value_or(0));
token[jss::seq] = txnSeq.value_or(0);
break;
}
if (foundResume)
{
int txnSize = 2048;
Blob rawTxn (txnSize);
txnSize = db->getBinary ("RawTxn", &rawTxn[0], rawTxn.size ());
Blob rawTxn;
if (soci::i_ok == rtI)
convert (sociTxnBlob, rawTxn);
Blob txnMeta;
if (soci::i_ok == tmI)
convert (sociTxnMetaBlob, txnMeta);
if (txnSize > rawTxn.size ())
{
rawTxn.resize (txnSize);
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.size ());
}
else
rawTxn.resize (txnSize);
int metaSize = 2048;
Blob rawMeta (metaSize);
metaSize = db->getBinary (
"TxnMeta", &rawMeta[0], rawMeta.size ());
if (metaSize > rawMeta.size ())
{
rawMeta.resize (metaSize);
db->getBinary (
"TxnMeta", &*rawMeta.begin (), rawMeta.size ());
}
else
{
rawMeta.resize (metaSize);
}
ret.emplace_back (strHex (rawTxn), strHex (rawMeta),
db->getInt ("LedgerSeq"));
ret.emplace_back (
strHex (rawTxn.begin (), rawTxn.size ()),
strHex (txnMeta.begin (), txnMeta.size ()),
rangeCheckedCast<std::int32_t>(ledgerSeq.value_or (0)));
--numberOfResults;
}
}
@@ -2275,11 +2293,20 @@ NetworkOPsImp::getLedgerAffectedAccounts (std::uint32_t ledgerSeq)
% ledgerSeq);
RippleAddress acct;
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
SQL_FOREACH (db, sql)
auto db = getApp().getTxnDB ().checkoutDb ();
soci::blob accountBlob(*db);
soci::indicator bi;
soci::statement st = (db->prepare << sql, soci::into(accountBlob, bi));
st.execute ();
std::string accountStr;
while (st.fetch ())
{
if (acct.setAccountID (db->getStrBinary ("Account")))
if (soci::i_ok == bi)
convert (accountBlob, accountStr);
else
accountStr.clear ();
if (acct.setAccountID (accountStr))
accounts.push_back (acct);
}
}

View File

@@ -26,6 +26,7 @@
#include <boost/format.hpp>
#include <beast/cxx14/memory.h> // <memory>
#include <boost/format.hpp>
#include <boost/optional.hpp>
namespace ripple {
void SHAMapStoreImp::SavedStateDB::init (BasicConfig const& config,
@@ -54,14 +55,14 @@ void SHAMapStoreImp::SavedStateDB::init (BasicConfig const& config,
;
std::int64_t count = 0;
soci::statement st = (session_.prepare <<
"SELECT COUNT(Key) FROM DbState WHERE Key = 1;"
, soci::into (count)
);
st.execute ();
if (!st.fetch ())
{
throw std::runtime_error("Failed to fetch Key Count from DbState.");
boost::optional<std::int64_t> countO;
session_ <<
"SELECT COUNT(Key) FROM DbState WHERE Key = 1;"
, soci::into (countO);
if (!countO)
throw std::runtime_error("Failed to fetch Key Count from DbState.");
count = *countO;
}
if (!count)
@@ -70,14 +71,15 @@ void SHAMapStoreImp::SavedStateDB::init (BasicConfig const& config,
"INSERT INTO DbState VALUES (1, '', '', 0);";
}
st = (session_.prepare <<
"SELECT COUNT(Key) FROM CanDelete WHERE Key = 1;"
, soci::into (count)
);
st.execute ();
if (!st.fetch ())
{
throw std::runtime_error ("Failed to fetch Key Count from CanDelete.");
boost::optional<std::int64_t> countO;
session_ <<
"SELECT COUNT(Key) FROM CanDelete WHERE Key = 1;"
, soci::into (countO);
if (!countO)
throw std::runtime_error("Failed to fetch Key Count from CanDelete.");
count = *countO;
}
if (!count)
@@ -507,14 +509,16 @@ SHAMapStoreImp::clearSql (DatabaseCon& database,
std::string const& deleteQuery)
{
LedgerIndex min = std::numeric_limits <LedgerIndex>::max();
Database* db = database.getDB();
std::unique_lock <std::recursive_mutex> lock (database.peekMutex());
if (!db->executeSQL (minQuery) || !db->startIterRows())
return;
min = db->getBigInt (0);
db->endIterRows ();
lock.unlock();
{
auto db = database.checkoutDb ();
boost::optional<std::uint64_t> m;
*db << minQuery, soci::into(m);
if (!m)
return;
min = *m;
}
if (health() != Health::ok)
return;
@@ -526,9 +530,10 @@ SHAMapStoreImp::clearSql (DatabaseCon& database,
{
min = (min + setup_.deleteBatch >= lastRotated) ? lastRotated :
min + setup_.deleteBatch;
lock.lock();
db->executeSQL (boost::str (formattedDeleteQuery % min));
lock.unlock();
{
auto db = database.checkoutDb ();
*db << boost::str (formattedDeleteQuery % min);
}
if (health())
return;
if (min < lastRotated)

View File

@@ -443,21 +443,21 @@ private:
{
ScopedUnlockType sul (mLock);
{
auto db = getApp().getLedgerDB ().getDB ();
auto dbl (getApp().getLedgerDB ().lock ());
auto db = getApp().getLedgerDB ().checkoutDb ();
Serializer s (1024);
db->executeSQL ("BEGIN TRANSACTION;");
soci::transaction tr(*db);
for (auto it: vector)
{
s.erase ();
it->add (s);
db->executeSQL (boost::str (
*db << boost::str (
insVal % to_string (it->getLedgerHash ()) %
it->getSignerPublic ().humanNodePublic () %
it->getSignTime () % sqlEscape (s.peekData ())));
it->getSignTime () % sqlEscape (s.peekData ()));
}
db->executeSQL ("END TRANSACTION;");
tr.commit ();
}
}
}

View File

@@ -38,6 +38,7 @@
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/format.hpp>
#include <boost/regex.hpp>
#include <boost/optional.hpp>
#include <fstream>
namespace ripple {
@@ -89,6 +90,73 @@ strJoin (Iterator first, Iterator last, std::string strSeperator)
return ossValues.str ();
}
template <size_t I, class String>
void selectBlobsIntoStrings (
soci::session& s,
String&& sql,
std::vector<std::array<boost::optional<std::string>, I>>& columns)
{
columns.clear ();
columns.reserve (32);
std::vector<soci::blob> blobs;
blobs.reserve (I);
for (int i = 0; i < I; ++i)
blobs.emplace_back (s);
std::array<soci::indicator, I> indicators;
std::string str;
soci::statement st = [&]
{
auto&& tmp = s.prepare << sql;
for (int i = 0; i < I; ++i)
tmp.operator, (soci::into (blobs[i], indicators[i]));
return tmp;
}();
st.execute ();
while (st.fetch ())
{
columns.emplace_back ();
for (int i = 0; i < I; ++i)
{
if (soci::i_ok == indicators[i])
{
convert (blobs[i], str);
columns.back ()[i] = str;
}
}
}
}
template<class TOther, class String>
void selectBlobsIntoStrings (
soci::session& s,
String&& sql,
std::vector<std::tuple<boost::optional<std::string>, boost::optional<TOther>>>& columns)
{
columns.clear ();
columns.reserve (32);
soci::blob blob(s);
soci::indicator ind;
boost::optional<TOther> other;
std::string str;
soci::statement st =
(s.prepare << sql, soci::into(blob, ind), soci::into(other));
st.execute ();
while (st.fetch ())
{
columns.emplace_back ();
if (soci::i_ok == ind)
{
convert (blob, str);
get<0>(columns.back ()) = str;
}
get<1>(columns.back ()) = other;
}
}
// VFALCO TODO move all function definitions inlined into the class.
class UniqueNodeListImp
: public UniqueNodeList
@@ -295,11 +363,14 @@ public:
void nodeRemovePublic (RippleAddress const& naNodePublic)
{
{
auto db = getApp().getWalletDB ().getDB ();
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL (str (boost::format ("DELETE FROM SeedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
db->executeSQL (str (boost::format ("DELETE FROM TrustedNodes WHERE PublicKey=%s") % sqlEscape (naNodePublic.humanNodePublic ())));
*db << str (
boost::format ("DELETE FROM SeedNodes WHERE PublicKey=%s") %
sqlEscape (naNodePublic.humanNodePublic ()));
*db << str (
boost::format ("DELETE FROM TrustedNodes WHERE PublicKey=%s") %
sqlEscape (naNodePublic.humanNodePublic ()));
}
// YYY Only dirty on successful delete.
@@ -317,10 +388,9 @@ public:
boost::to_lower (strDomain);
{
auto db = getApp().getWalletDB ().getDB ();
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL (str (boost::format ("DELETE FROM SeedDomains WHERE Domain=%s") % sqlEscape (strDomain)));
*db << str (boost::format ("DELETE FROM SeedDomains WHERE Domain=%s") % sqlEscape (strDomain));
}
// YYY Only dirty on successful delete.
@@ -332,13 +402,10 @@ public:
void nodeReset ()
{
{
auto db = getApp().getWalletDB ().getDB ();
auto db = getApp().getWalletDB ().checkoutDb ();
auto sl (getApp().getWalletDB ().lock ());
// XXX Check results.
db->executeSQL ("DELETE FROM SeedDomains");
db->executeSQL ("DELETE FROM SeedNodes");
*db << "DELETE FROM SeedDomains";
*db << "DELETE FROM SeedNodes";
}
fetchDirty ();
@@ -606,17 +673,22 @@ public:
Json::Value getUnlJson ()
{
auto db = getApp().getWalletDB ().getDB ();
Json::Value ret (Json::arrayValue);
auto sl (getApp().getWalletDB ().lock ());
SQL_FOREACH (db, "SELECT * FROM TrustedNodes;")
auto db = getApp().getWalletDB ().checkoutDb ();
std::vector<std::array<boost::optional<std::string>, 2>> columns;
selectBlobsIntoStrings(*db,
"SELECT PublicKey, Comment FROM TrustedNodes;",
columns);
for(auto const& strArray : columns)
{
Json::Value node (Json::objectValue);
node["publicKey"] = db->getStrBinary ("PublicKey");
node["comment"] = db->getStrBinary ("Comment");
node["publicKey"] = strArray[0].value_or("");
node["comment"] = strArray[1].value_or("");
ret.append (node);
}
@@ -673,22 +745,18 @@ private:
// Load information about when we last updated.
bool miscLoad ()
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().getDB ();
auto db = getApp().getWalletDB ().checkoutDb ();
if (!db->executeSQL ("SELECT * FROM Misc WHERE Magic=1;"))
boost::optional<int> suO, fuO;
*db << "SELECT ScoreUpdated, FetchUpdated FROM Misc WHERE Magic=1;",
soci::into(suO), soci::into(fuO);
if (!db->got_data() )
return false;
bool const bAvail = db->startIterRows ();
mtpFetchUpdated = ptFromSeconds (bAvail
? db->getInt ("FetchUpdated")
: -1);
mtpScoreUpdated = ptFromSeconds (bAvail
? db->getInt ("ScoreUpdated")
: -1);
db->endIterRows ();
mtpFetchUpdated = ptFromSeconds (fuO.value_or(-1));
mtpScoreUpdated = ptFromSeconds (suO.value_or(-1));
trustedLoad ();
@@ -700,12 +768,11 @@ private:
// Persist update information.
bool miscSave ()
{
auto db = getApp().getWalletDB ().getDB ();
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL (str (boost::format ("REPLACE INTO Misc (Magic,FetchUpdated,ScoreUpdated) VALUES (1,%d,%d);")
% iToSeconds (mtpFetchUpdated)
% iToSeconds (mtpScoreUpdated)));
*db << str (boost::format ("REPLACE INTO Misc (Magic,FetchUpdated,ScoreUpdated) VALUES (1,%d,%d);")
% iToSeconds (mtpFetchUpdated)
% iToSeconds (mtpScoreUpdated));
return true;
}
@@ -730,16 +797,18 @@ private:
WriteLog (lsWARNING, UniqueNodeList) << "Entry in cluster list invalid: '" << c << "'";
}
auto db = getApp().getWalletDB ().getDB ();
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
ScopedUNLLockType slUNL (mUNLLock);
mUNL.clear ();
// XXX Needs to limit by quanity and quality.
SQL_FOREACH (db, "SELECT PublicKey FROM TrustedNodes WHERE Score != 0;")
std::vector<std::array<boost::optional<std::string>, 1>> columns;
selectBlobsIntoStrings(*db,
"SELECT PublicKey FROM TrustedNodes WHERE Score != 0;",
columns);
for(auto const& strArray : columns)
{
mUNL.insert (db->getStrBinary ("PublicKey"));
mUNL.insert (strArray[0].value_or(""));
}
}
@@ -828,56 +897,58 @@ private:
hash_map<std::string, int> umDomainIdx; // Map of domain to index.
std::vector<scoreNode> vsnNodes; // Index to scoring node.
auto db = getApp().getWalletDB ().getDB ();
// For each entry in SeedDomains with a PublicKey:
// - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
SQL_FOREACH (db, "SELECT Domain,PublicKey,Source FROM SeedDomains;")
std::vector<std::array<boost::optional<std::string>, 3>> columns;
selectBlobsIntoStrings(*db,
"SELECT Domain,PublicKey,Source FROM SeedDomains;",
columns);
for(auto const& strArray : columns)
{
if (db->getNull ("PublicKey"))
{
if (!strArray[1])
// We ignore entries we don't have public keys for.
continue;
std::string const strDomain = strArray[0].value_or("");
std::string const strPublicKey = *strArray[1];
std::string const strSource = strArray[2].value_or("");
assert (!strSource.empty ());
int const iScore = iSourceScore (static_cast<ValidatorSource> (strSource[0]));
auto siOld = umPulicIdx.find (strPublicKey);
if (siOld == umPulicIdx.end ())
{
// New node
int iNode = vsnNodes.size ();
umPulicIdx[strPublicKey] = iNode;
umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
snCurrent.strValidator = strPublicKey;
snCurrent.iScore = iScore;
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back (snCurrent);
}
else
{
std::string strDomain = db->getStrBinary ("Domain");
std::string strPublicKey = db->getStrBinary ("PublicKey");
std::string strSource = db->getStrBinary ("Source");
int iScore = iSourceScore (static_cast<ValidatorSource> (strSource[0]));
auto siOld = umPulicIdx.find (strPublicKey);
scoreNode& snOld = vsnNodes[siOld->second];
if (siOld == umPulicIdx.end ())
if (snOld.iScore < iScore)
{
// New node
int iNode = vsnNodes.size ();
// Update old node
umPulicIdx[strPublicKey] = iNode;
umDomainIdx[strDomain] = iNode;
scoreNode snCurrent;
snCurrent.strValidator = strPublicKey;
snCurrent.iScore = iScore;
snCurrent.iRoundSeed = snCurrent.iScore;
snCurrent.iRoundScore = 0;
snCurrent.iSeen = -1;
vsnNodes.push_back (snCurrent);
}
else
{
scoreNode& snOld = vsnNodes[siOld->second];
if (snOld.iScore < iScore)
{
// Update old node
snOld.iScore = iScore;
snOld.iRoundSeed = snOld.iScore;
}
snOld.iScore = iScore;
snOld.iRoundSeed = snOld.iScore;
}
}
}
@@ -886,12 +957,17 @@ private:
// For each entry in SeedNodes:
// - Add an entry in umPulicIdx, umDomainIdx, and vsnNodes.
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
SQL_FOREACH (db, "SELECT PublicKey,Source FROM SeedNodes;")
std::vector<std::array<boost::optional<std::string>, 2>> columns;
selectBlobsIntoStrings(*db,
"SELECT PublicKey,Source FROM SeedNodes;",
columns);
for(auto const& strArray : columns)
{
std::string strPublicKey = db->getStrBinary ("PublicKey");
std::string strSource = db->getStrBinary ("Source");
std::string strPublicKey = strArray[0].value_or("");
std::string strSource = strArray[1].value_or("");
assert (!strSource.empty ());
int iScore = iSourceScore (static_cast<ValidatorSource> (strSource[0]));
auto siOld = umPulicIdx.find (strPublicKey);
@@ -950,12 +1026,20 @@ private:
std::string& strValidator = sn.strValidator;
std::vector<int>& viReferrals = sn.viReferrals;
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
SQL_FOREACH (db, boost::str (boost::format ("SELECT Referral FROM ValidatorReferrals WHERE Validator=%s ORDER BY Entry;")
% sqlEscape (strValidator)))
std::vector<std::array<boost::optional<std::string>, 1>> columns;
selectBlobsIntoStrings(*db,
boost::str (boost::format (
"SELECT Referral FROM ValidatorReferrals "
"WHERE Validator=%s ORDER BY Entry;") %
sqlEscape (strValidator)),
columns);
std::string strReferral;
for(auto const& strArray : columns)
{
std::string strReferral = db->getStrBinary ("Referral");
strReferral = strArray[0].value_or("");
int iReferral;
RippleAddress na;
@@ -1024,10 +1108,10 @@ private:
}
// Persist validator scores.
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL ("BEGIN;");
db->executeSQL ("UPDATE TrustedNodes SET Score = 0 WHERE Score != 0;");
soci::transaction tr(*db);
*db << "UPDATE TrustedNodes SET Score = 0 WHERE Score != 0;";
if (!vsnNodes.empty ())
{
@@ -1041,10 +1125,25 @@ private:
vstrPublicKeys[iNode] = sqlEscape (vsnNodes[iNode].strValidator);
}
SQL_FOREACH (db, str (boost::format ("SELECT PublicKey,Seen FROM TrustedNodes WHERE PublicKey IN (%s);")
% strJoin (vstrPublicKeys.begin (), vstrPublicKeys.end (), ",")))
// Iterate through the result rows with a fectch b/c putting a
// column of type DATETIME into a boost::tuple can throw when the
// datetime column is invalid (even if the value as int is valid).
std::vector<std::tuple<boost::optional<std::string>,
boost::optional<int>>> columns;
selectBlobsIntoStrings (
*db,
str (boost::format (
"SELECT PublicKey,Seen FROM TrustedNodes WHERE "
"PublicKey IN (%s);") %
strJoin (
vstrPublicKeys.begin (), vstrPublicKeys.end (), ",")),
columns);
std::string pk;
for(auto const& col : columns)
{
vsnNodes[umPulicIdx[db->getStrBinary ("PublicKey")]].iSeen = db->getNull ("Seen") ? -1 : db->getInt ("Seen");
pk = get<0>(col).value_or ("");
vsnNodes[umPulicIdx[pk]].iSeen = get<1>(col).value_or (-1);
}
}
@@ -1070,8 +1169,8 @@ private:
usUNL.insert (sn.strValidator);
}
db->executeSQL (str (boost::format ("REPLACE INTO TrustedNodes (PublicKey,Score,Seen) VALUES %s;")
% strJoin (vstrValues.begin (), vstrValues.end (), ",")));
*db << str (boost::format ("REPLACE INTO TrustedNodes (PublicKey,Score,Seen) VALUES %s;")
% strJoin (vstrValues.begin (), vstrValues.end (), ","));
}
{
@@ -1085,12 +1184,17 @@ private:
if (!vsnNodes.empty ())
{
std::vector<std::string> vstrPublicKeys;
// For every IpReferral add a score for the IP and PORT.
SQL_FOREACH (db, "SELECT Validator,COUNT(*) AS Count FROM IpReferrals GROUP BY Validator;")
std::vector<std::tuple<boost::optional<std::string>,
boost::optional<std::int32_t>>> columns;
selectBlobsIntoStrings (
*db,
"SELECT Validator,COUNT(*) AS Count FROM "
"IpReferrals GROUP BY Validator;",
columns);
for(auto const& col : columns)
{
umValidators[db->getStrBinary ("Validator")] = db->getInt ("Count");
umValidators[get<0>(col).value_or("")] = get<1>(col).value_or(0);
// WriteLog (lsTRACE, UniqueNodeList) << strValidator << ":" << db->getInt("Count");
}
@@ -1114,15 +1218,23 @@ private:
score iBase = iSeed * iEntries / iTotal;
int iEntry = 0;
SQL_FOREACH (db, str (boost::format ("SELECT IP,Port FROM IpReferrals WHERE Validator=%s ORDER BY Entry;")
% sqlEscape (strValidator)))
std::vector<std::tuple<boost::optional<std::string>,
boost::optional<std::int32_t>>> columns;
selectBlobsIntoStrings (
*db,
str (boost::format (
"SELECT IP,Port FROM IpReferrals WHERE "
"Validator=%s ORDER BY Entry;") %
sqlEscape (strValidator)),
columns);
for(auto const& col : columns)
{
score iPoints = iBase * (iEntries - iEntry) / iEntries;
int iPort;
iPort = db->getNull ("Port") ? -1 : db->getInt ("Port");
iPort = get<1>(col).value_or(0);
std::pair< std::string, int> ep = std::make_pair (db->getStrBinary ("IP"), iPort);
std::pair< std::string, int> ep = std::make_pair (get<0>(col).value_or(""), iPort);
auto itEp = umScore.find (ep);
@@ -1132,7 +1244,7 @@ private:
}
}
db->executeSQL ("COMMIT;");
tr.commit ();
}
//--------------------------------------------------------------------------
@@ -1305,20 +1417,28 @@ private:
boost::posix_time::ptime tpNext (boost::posix_time::min_date_time);
boost::posix_time::ptime tpNow (boost::posix_time::second_clock::universal_time ());
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().getDB ();
auto db = getApp().getWalletDB ().checkoutDb ();
if (db->executeSQL ("SELECT Domain,Next FROM SeedDomains INDEXED BY SeedDomainNext ORDER BY Next LIMIT 1;")
&& db->startIterRows ())
{
int iNext (db->getInt ("Next"));
soci::blob b(*db);
soci::indicator ind;
boost::optional<int> nO;
*db << "SELECT Domain,Next FROM SeedDomains INDEXED BY SeedDomainNext ORDER BY Next LIMIT 1;",
soci::into(b, ind),
soci::into(nO);
if (nO)
{
int iNext (*nO);
tpNext = ptFromSeconds (iNext);
tpNext = ptFromSeconds (iNext);
WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow);
strDomain = db->getStrBinary ("Domain");
db->endIterRows ();
WriteLog (lsTRACE, UniqueNodeList) << str (boost::format ("fetchNext: iNext=%s tpNext=%s tpNow=%s") % iNext % tpNext % tpNow);
if (soci::i_ok == ind)
convert (b, strDomain);
else
strDomain.clear ();
}
}
if (!strDomain.empty ())
@@ -1557,8 +1677,6 @@ private:
// --> naNodePublic: public key of the validating node.
void processIps (std::string const& strSite, RippleAddress const& naNodePublic, IniFileSections::mapped_type* pmtVecStrIps)
{
auto db = getApp().getWalletDB ().getDB ();
std::string strEscNodePublic = sqlEscape (naNodePublic.humanNodePublic ());
WriteLog (lsDEBUG, UniqueNodeList)
@@ -1567,9 +1685,8 @@ private:
// Remove all current Validator's entries in IpReferrals
{
auto sl (getApp().getWalletDB ().lock ());
db->executeSQL (str (boost::format ("DELETE FROM IpReferrals WHERE Validator=%s;") % strEscNodePublic));
// XXX Check result.
auto db = getApp().getWalletDB ().checkoutDb ();
*db << str (boost::format ("DELETE FROM IpReferrals WHERE Validator=%s;") % strEscNodePublic);
}
// Add new referral entries.
@@ -1610,9 +1727,9 @@ private:
{
vstrValues.resize (iValues);
auto sl (getApp().getWalletDB ().lock ());
db->executeSQL (str (boost::format ("INSERT INTO IpReferrals (Validator,Entry,IP,Port) VALUES %s;")
% strJoin (vstrValues.begin (), vstrValues.end (), ",")));
auto db = getApp().getWalletDB ().checkoutDb ();
*db << str (boost::format ("INSERT INTO IpReferrals (Validator,Entry,IP,Port) VALUES %s;")
% strJoin (vstrValues.begin (), vstrValues.end (), ","));
// XXX Check result.
}
}
@@ -1629,7 +1746,6 @@ private:
// --> vsWhy: reason for adding validator to SeedDomains or SeedNodes.
int processValidators (std::string const& strSite, std::string const& strValidatorsSrc, RippleAddress const& naNodePublic, ValidatorSource vsWhy, IniFileSections::mapped_type* pmtVecStrValidators)
{
auto db = getApp().getWalletDB ().getDB ();
std::string strNodePublic = naNodePublic.isValid () ? naNodePublic.humanNodePublic () : strValidatorsSrc;
int iValues = 0;
@@ -1641,9 +1757,9 @@ private:
// Remove all current Validator's entries in ValidatorReferrals
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL (str (boost::format ("DELETE FROM ValidatorReferrals WHERE Validator='%s';") % strNodePublic));
*db << str (boost::format ("DELETE FROM ValidatorReferrals WHERE Validator='%s';") % strNodePublic);
// XXX Check result.
}
@@ -1712,9 +1828,9 @@ private:
std::string strSql = str (boost::format ("INSERT INTO ValidatorReferrals (Validator,Entry,Referral) VALUES %s;")
% strJoin (vstrValues.begin (), vstrValues.end (), ","));
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
db->executeSQL (strSql);
*db << strSql;
// XXX Check result.
}
}
@@ -1759,57 +1875,79 @@ private:
// Retrieve a SeedDomain from DB.
bool getSeedDomains (std::string const& strDomain, seedDomain& dstSeedDomain)
{
bool bResult;
auto db = getApp().getWalletDB ().getDB ();
bool bResult = false;
std::string strSql = boost::str (boost::format ("SELECT * FROM SeedDomains WHERE Domain=%s;")
% sqlEscape (strDomain));
std::string strSql = boost::str (
boost::format (
"SELECT Domain, PublicKey, Source, Next, Scan, Fetch, Sha256, "
"Comment FROM SeedDomains WHERE Domain=%s;") %
sqlEscape (strDomain));
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
bResult = db->executeSQL (strSql) && db->startIterRows ();
// Iterate through the result rows with a fectch b/c putting a
// column of type DATETIME into a boost::tuple can throw when the
// datetime column is invalid (even if the value as int is valid).
soci::blob domainBlob(*db);
soci::indicator di;
boost::optional<std::string> strPublicKey;
soci:: blob sourceBlob(*db);
soci::indicator si;
std::string strSource;
boost::optional<int> iNext;
boost::optional<int> iScan;
boost::optional<int> iFetch;
boost::optional<std::string> strSha256;
soci::blob commentBlob(*db);
soci::indicator ci;
boost::optional<std::string> strComment;
if (bResult)
soci::statement st = (db->prepare << strSql,
soci::into (domainBlob, di),
soci::into (strPublicKey),
soci::into (sourceBlob, si),
soci::into (iNext),
soci::into (iScan),
soci::into (iFetch),
soci::into (strSha256),
soci::into (commentBlob, ci));
st.execute ();
while (st.fetch ())
{
std::string strPublicKey;
int iNext;
int iScan;
int iFetch;
std::string strSha256;
bResult = true;
dstSeedDomain.strDomain = db->getStrBinary ("Domain");
if (soci::i_ok == di)
convert (domainBlob, dstSeedDomain.strDomain);
if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
{
dstSeedDomain.naPublicKey.setNodePublic (strPublicKey);
}
if (strPublicKey && !strPublicKey->empty ())
dstSeedDomain.naPublicKey.setNodePublic (*strPublicKey);
else
{
dstSeedDomain.naPublicKey.clear ();
}
std::string strSource = db->getStrBinary ("Source");
dstSeedDomain.vsSource = static_cast<ValidatorSource> (strSource[0]);
iNext = db->getInt ("Next");
dstSeedDomain.tpNext = ptFromSeconds (iNext);
iScan = db->getInt ("Scan");
dstSeedDomain.tpScan = ptFromSeconds (iScan);
iFetch = db->getInt ("Fetch");
dstSeedDomain.tpFetch = ptFromSeconds (iFetch);
if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
if (soci::i_ok == si)
{
dstSeedDomain.iSha256.SetHex (strSha256);
convert (sourceBlob, strSource);
dstSeedDomain.vsSource = static_cast<ValidatorSource> (strSource[0]);
}
else
{
dstSeedDomain.iSha256.zero ();
assert (0);
}
dstSeedDomain.strComment = db->getStrBinary ("Comment");
dstSeedDomain.tpNext = ptFromSeconds (iNext.value_or (0));
dstSeedDomain.tpScan = ptFromSeconds (iScan.value_or (0));
dstSeedDomain.tpFetch = ptFromSeconds (iFetch.value_or (0));
db->endIterRows ();
if (strSha256 && !strSha256->empty ())
dstSeedDomain.iSha256.SetHex (*strSha256);
else
dstSeedDomain.iSha256.zero ();
if (soci::i_ok == ci)
convert (commentBlob, dstSeedDomain.strComment);
else
dstSeedDomain.strComment.clear ();
}
return bResult;
@@ -1820,8 +1958,6 @@ private:
// Persist a SeedDomain.
void setSeedDomains (const seedDomain& sdSource, bool bNext)
{
auto db = getApp().getWalletDB ().getDB ();
int iNext = iToSeconds (sdSource.tpNext);
int iScan = iToSeconds (sdSource.tpScan);
int iFetch = iToSeconds (sdSource.tpFetch);
@@ -1839,12 +1975,16 @@ private:
% sqlEscape (sdSource.strComment)
);
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
if (!db->executeSQL (strSql))
try
{
*db << strSql;
}
catch (soci::soci_error& e)
{
// XXX Check result.
WriteLog (lsWARNING, UniqueNodeList) << "setSeedDomains: failed.";
WriteLog (lsWARNING, UniqueNodeList) << "setSeedDomains: failed. Error: " << e.what();
}
if (bNext && (mtpFetchNext.is_not_a_date_time () || mtpFetchNext > sdSource.tpNext))
@@ -1860,59 +2000,65 @@ private:
// Retrieve a SeedNode from DB.
bool getSeedNodes (RippleAddress const& naNodePublic, seedNode& dstSeedNode)
{
bool bResult;
auto db = getApp().getWalletDB ().getDB ();
std::string strSql =
str (boost::format (
"SELECT PublicKey, Source, Next, Scan, Fetch, Sha256, "
"Comment FROM SeedNodes WHERE PublicKey='%s';") %
naNodePublic.humanNodePublic ());
std::string strSql = str (boost::format ("SELECT * FROM SeedNodes WHERE PublicKey='%s';")
% naNodePublic.humanNodePublic ());
auto db = getApp().getWalletDB ().checkoutDb ();
auto sl (getApp().getWalletDB ().lock ());
std::string strPublicKey;
std::string strSource;
soci::blob sourceBlob(*db);
soci::indicator si;
boost::optional<int> iNext;
boost::optional<int> iScan;
boost::optional<int> iFetch;
boost::optional<std::string> strSha256;
soci::blob commentBlob(*db);
soci::indicator ci;
bResult = db->executeSQL (strSql) && db->startIterRows ();
*db << strSql,
soci::into (strPublicKey),
soci::into (sourceBlob, si),
soci::into (iNext),
soci::into (iScan),
soci::into (iFetch),
soci::into (strSha256),
soci::into (commentBlob, ci);
if (bResult)
if (!db->got_data ())
return false;
if (!strPublicKey.empty ())
dstSeedNode.naPublicKey.setNodePublic (strPublicKey);
else
dstSeedNode.naPublicKey.clear ();
if (soci::i_ok == si)
{
std::string strPublicKey;
std::string strSource;
int iNext;
int iScan;
int iFetch;
std::string strSha256;
if (!db->getNull ("PublicKey") && db->getStr ("PublicKey", strPublicKey))
{
dstSeedNode.naPublicKey.setNodePublic (strPublicKey);
}
else
{
dstSeedNode.naPublicKey.clear ();
}
strSource = db->getStrBinary ("Source");
convert (sourceBlob, strSource);
dstSeedNode.vsSource = static_cast<ValidatorSource> (strSource[0]);
iNext = db->getInt ("Next");
dstSeedNode.tpNext = ptFromSeconds (iNext);
iScan = db->getInt ("Scan");
dstSeedNode.tpScan = ptFromSeconds (iScan);
iFetch = db->getInt ("Fetch");
dstSeedNode.tpFetch = ptFromSeconds (iFetch);
if (!db->getNull ("Sha256") && db->getStr ("Sha256", strSha256))
{
dstSeedNode.iSha256.SetHex (strSha256);
}
else
{
dstSeedNode.iSha256.zero ();
}
dstSeedNode.strComment = db->getStrBinary ("Comment");
db->endIterRows ();
}
else
assert (0);
return bResult;
dstSeedNode.tpNext = ptFromSeconds (iNext.value_or(0));
dstSeedNode.tpScan = ptFromSeconds (iScan.value_or(0));
dstSeedNode.tpFetch = ptFromSeconds (iFetch.value_or(0));
if (strSha256 && !strSha256->empty ())
dstSeedNode.iSha256.SetHex (*strSha256);
else
dstSeedNode.iSha256.zero ();
if (soci::i_ok == ci)
convert (commentBlob, dstSeedNode.strComment);
else
dstSeedNode.strComment.clear ();
return true;
}
//--------------------------------------------------------------------------
@@ -1921,8 +2067,6 @@ private:
// <-- bNext: true, to do fetching if needed.
void setSeedNodes (const seedNode& snSource, bool bNext)
{
auto db = getApp().getWalletDB ().getDB ();
int iNext = iToSeconds (snSource.tpNext);
int iScan = iToSeconds (snSource.tpScan);
int iFetch = iToSeconds (snSource.tpFetch);
@@ -1942,12 +2086,15 @@ private:
);
{
auto sl (getApp().getWalletDB ().lock ());
auto db = getApp().getWalletDB ().checkoutDb ();
if (!db->executeSQL (strSql))
try
{
// XXX Check result.
WriteLog (lsTRACE, UniqueNodeList) << "setSeedNodes: failed.";
*db << strSql;
}
catch(soci::soci_error& e)
{
WriteLog (lsTRACE, UniqueNodeList) << "setSeedNodes: failed. Error: " << e.what ();
}
}

View File

@@ -24,6 +24,7 @@
#include <ripple/app/ledger/LedgerMaster.h>
#include <ripple/app/main/Application.h>
#include <ripple/protocol/JsonFields.h>
#include <boost/optional.hpp>
namespace ripple {
@@ -91,26 +92,13 @@ void Transaction::setStatus (TransStatus ts, std::uint32_t lseq)
}
Transaction::pointer Transaction::transactionFromSQL (
Database* db, Validate validate)
boost::optional<std::uint64_t> const& ledgerSeq,
boost::optional<std::string> const& status,
Blob const& rawTxn,
Validate validate)
{
Serializer rawTxn;
std::string status;
std::uint32_t inLedger;
int txSize = 2048;
rawTxn.resize (txSize);
db->getStr ("Status", status);
inLedger = db->getInt ("LedgerSeq");
txSize = db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
if (txSize > rawTxn.getLength ())
{
rawTxn.resize (txSize);
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
}
rawTxn.resize (txSize);
std::uint32_t const inLedger =
rangeCheckedCast<std::uint32_t>(ledgerSeq.value_or (0));
SerialIter it (rawTxn);
auto txn = std::make_shared<STTx> (it);
@@ -118,7 +106,9 @@ Transaction::pointer Transaction::transactionFromSQL (
TransStatus st (INVALID);
switch (status[0])
char const statusChar = status ? (*status)[0] : TXN_SQL_UNKNOWN;
switch (statusChar)
{
case TXN_SQL_NEW:
st = NEW;
@@ -152,86 +142,29 @@ Transaction::pointer Transaction::transactionFromSQL (
return tr;
}
// DAVID: would you rather duplicate this code or keep the lock longer?
Transaction::pointer Transaction::transactionFromSQL (std::string const& sql)
{
Serializer rawTxn;
std::string status;
std::uint32_t inLedger;
int txSize = 2048;
rawTxn.resize (txSize);
{
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().getDB ();
if (!db->executeSQL (sql, true) || !db->startIterRows ())
return Transaction::pointer ();
db->getStr ("Status", status);
inLedger = db->getInt ("LedgerSeq");
txSize = db->getBinary (
"RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
if (txSize > rawTxn.getLength ())
{
rawTxn.resize (txSize);
db->getBinary ("RawTxn", &*rawTxn.begin (), rawTxn.getLength ());
}
db->endIterRows ();
}
rawTxn.resize (txSize);
SerialIter it (rawTxn);
auto txn = std::make_shared<STTx> (it);
auto tr = std::make_shared<Transaction> (txn, Validate::YES);
TransStatus st (INVALID);
switch (status[0])
{
case TXN_SQL_NEW:
st = NEW;
break;
case TXN_SQL_CONFLICT:
st = CONFLICTED;
break;
case TXN_SQL_HELD:
st = HELD;
break;
case TXN_SQL_VALIDATED:
st = COMMITTED;
break;
case TXN_SQL_INCLUDED:
st = INCLUDED;
break;
case TXN_SQL_UNKNOWN:
break;
default:
assert (false);
}
tr->setStatus (st);
tr->setLedger (inLedger);
return tr;
}
Transaction::pointer Transaction::load (uint256 const& id)
{
std::string sql = "SELECT LedgerSeq,Status,RawTxn "
"FROM Transactions WHERE TransID='";
sql.append (to_string (id));
sql.append ("';");
return transactionFromSQL (sql);
boost::optional<std::uint64_t> ledgerSeq;
boost::optional<std::string> status;
Blob rawTxn;
{
auto db = getApp().getTxnDB ().checkoutDb ();
soci::blob sociRawTxnBlob (*db);
soci::indicator rti;
*db << sql, soci::into (ledgerSeq), soci::into (status),
soci::into (sociRawTxnBlob, rti);
if (db->got_data () && soci::i_ok == rti)
convert(sociRawTxnBlob, rawTxn);
}
return Transaction::transactionFromSQL (
ledgerSeq, status, rawTxn, Validate::YES);
}
// options 1 to include the date of the transaction

View File

@@ -23,6 +23,7 @@
#include <ripple/protocol/Protocol.h>
#include <ripple/protocol/STTx.h>
#include <ripple/protocol/TER.h>
#include <boost/optional.hpp>
namespace ripple {
@@ -64,7 +65,11 @@ public:
Transaction (STTx::ref, Validate);
static Transaction::pointer sharedTransaction (Blob const&, Validate);
static Transaction::pointer transactionFromSQL (Database*, Validate);
static Transaction::pointer transactionFromSQL (
boost::optional<std::uint64_t> const& ledgerSeq,
boost::optional<std::string> const& status,
Blob const& rawTxn,
Validate validate);
bool checkSign () const;
@@ -116,9 +121,6 @@ public:
static bool isHexTxID (std::string const&);
protected:
static Transaction::pointer transactionFromSQL (std::string const&);
private:
uint256 mTransactionID;
RippleAddress mAccountFrom;

View File

@@ -28,10 +28,12 @@ namespace ripple {
// VFALCO TODO rename class to TransactionMeta
TransactionMetaSet::TransactionMetaSet (uint256 const& txid, std::uint32_t ledger, Blob const& vec) :
template<class T>
TransactionMetaSet::TransactionMetaSet (uint256 const& txid, std::uint32_t ledger, T const& data,
CtorHelper) :
mTransactionID (txid), mLedger (ledger), mNodes (sfAffectedNodes, 32)
{
Serializer s (vec);
Serializer s (data);
SerialIter sit (s);
std::unique_ptr<STBase> pobj = STObject::deserialize (sit, sfMetadata);
@@ -48,6 +50,20 @@ TransactionMetaSet::TransactionMetaSet (uint256 const& txid, std::uint32_t ledge
setDeliveredAmount (obj->getFieldAmount (sfDeliveredAmount));
}
TransactionMetaSet::TransactionMetaSet (uint256 const& txid,
std::uint32_t ledger,
Blob const& vec)
: TransactionMetaSet (txid, ledger, vec, CtorHelper ())
{
}
TransactionMetaSet::TransactionMetaSet (uint256 const& txid,
std::uint32_t ledger,
std::string const& data)
: TransactionMetaSet (txid, ledger, data, CtorHelper ())
{
}
bool TransactionMetaSet::isNodeAffected (uint256 const& node) const
{
for (auto const& n : mNodes)

View File

@@ -33,6 +33,11 @@ public:
typedef std::shared_ptr<TransactionMetaSet> pointer;
typedef const pointer& ref;
private:
struct CtorHelper{};
template<class T>
TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, T const& data,
CtorHelper);
public:
TransactionMetaSet ()
: mLedger (0)
@@ -50,6 +55,7 @@ public:
}
TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, Blob const&);
TransactionMetaSet (uint256 const& txID, std::uint32_t ledger, std::string const&);
void init (uint256 const& transactionID, std::uint32_t ledger);
void clear ()

View File

@@ -291,6 +291,24 @@ get (Section const& section,
return defaultValue;
}
inline
std::string
get (Section const& section,
std::string const& name, const char* defaultValue)
{
auto const result = section.find (name);
if (! result.second)
return defaultValue;
try
{
return boost::lexical_cast <std::string> (result.first);
}
catch(...)
{
}
return defaultValue;
}
template <class T>
bool
get_if_exists (Section const& section,

View File

@@ -22,6 +22,7 @@
#include <ripple/app/data/SociDB.h>
#include <beast/utility/Debug.h>
#include <boost/optional.hpp>
namespace ripple {
namespace PeerFinder {
@@ -103,26 +104,29 @@ public:
soci::transaction tr (m_session);
m_session <<
"DELETE FROM PeerFinder_BootstrapCache";
std::string s;
int valence;
soci::statement st = (m_session.prepare <<
"INSERT INTO PeerFinder_BootstrapCache ( "
" address, "
" valence "
") VALUES ( "
" :s, :valence "
");"
, soci::use (s)
, soci::use (valence)
);
for (auto const& e : v)
if (!v.empty ())
{
s = to_string (e.endpoint);
valence = e.valence;
st.execute ();
st.fetch ();
std::vector<std::string> s;
std::vector<int> valence;
s.reserve (v.size ());
valence.reserve (v.size ());
for (auto const& e : v)
{
s.emplace_back (to_string (e.endpoint));
valence.emplace_back (e.valence);
}
m_session <<
"INSERT INTO PeerFinder_BootstrapCache ( "
" address, "
" valence "
") VALUES ( "
" :s, :valence "
");"
, soci::use (s)
, soci::use (valence);
}
tr.commit ();
@@ -136,16 +140,16 @@ public:
// get version
int version (0);
{
boost::optional<int> vO;
m_session <<
"SELECT "
" version "
"FROM SchemaVersion WHERE "
" name = 'PeerFinder'"
, soci::into (version)
, soci::into (vO)
;
if (!m_session.got_data ())
version = 0;
version = vO.value_or (0);
m_journal.info <<
"Opened version " << version << " database";
@@ -222,10 +226,21 @@ public:
}
}
if (!list.empty ())
{
std::string s;
int valence;
soci::statement st = (m_session.prepare <<
std::vector<std::string> s;
std::vector<int> valence;
s.reserve (list.size ());
valence.reserve (list.size ());
for (auto iter (list.cbegin ());
iter != list.cend (); ++iter)
{
s.emplace_back (to_string (iter->endpoint));
valence.emplace_back (iter->valence);
}
m_session <<
"INSERT INTO PeerFinder_BootstrapCache_Next ( "
" address, "
" valence "
@@ -234,16 +249,8 @@ public:
");"
, soci::use (s)
, soci::use (valence)
);
;
for (auto iter (list.cbegin ());
iter != list.cend (); ++iter)
{
s = to_string (iter->endpoint);
valence = iter->valence;
st.execute ();
st.fetch ();
}
}
m_session <<

View File

@@ -49,17 +49,17 @@ Json::Value doGetCounts (RPC::Context& context)
Application& app = getApp();
int dbKB = app.getLedgerDB ().getDB ()->getKBUsedAll ();
int dbKB = getKBUsedAll (app.getLedgerDB ().getSession ());
if (dbKB > 0)
ret[jss::dbKBTotal] = dbKB;
dbKB = app.getLedgerDB ().getDB ()->getKBUsedDB ();
dbKB = getKBUsedDB (app.getLedgerDB ().getSession ());
if (dbKB > 0)
ret[jss::dbKBLedger] = dbKB;
dbKB = app.getTxnDB ().getDB ()->getKBUsedDB ();
dbKB = getKBUsedDB (app.getTxnDB ().getSession ());
if (dbKB > 0)
ret[jss::dbKBTransaction] = dbKB;

View File

@@ -44,16 +44,34 @@ Json::Value doTxHistory (RPC::Context& context)
std::string sql =
boost::str (boost::format (
"SELECT * FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20")
"SELECT LedgerSeq, Status, RawTxn "
"FROM Transactions ORDER BY LedgerSeq desc LIMIT %u,20")
% startIndex);
{
auto db = getApp().getTxnDB ().getDB ();
auto sl (getApp().getTxnDB ().lock ());
auto db = getApp().getTxnDB ().checkoutDb ();
SQL_FOREACH (db, sql)
boost::optional<std::uint64_t> ledgerSeq;
boost::optional<std::string> status;
soci::blob sociRawTxnBlob (*db);
soci::indicator rti;
Blob rawTxn;
soci::statement st = (db->prepare << sql,
soci::into (ledgerSeq),
soci::into (status),
soci::into (sociRawTxnBlob, rti));
st.execute ();
while (st.fetch ())
{
if (auto trans = Transaction::transactionFromSQL (db, Validate::NO))
if (soci::i_ok == rti)
convert(sociRawTxnBlob, rawTxn);
else
rawTxn.clear ();
if (auto trans = Transaction::transactionFromSQL (
ledgerSeq, status, rawTxn, Validate::NO))
txs.append (trans->getJson (0));
}
}

View File

@@ -1,48 +0,0 @@
//------------------------------------------------------------------------------
/*
This file is part of rippled: https://github.com/ripple/rippled
Copyright (c) 2012-2015 Ripple Labs Inc.
Permission to use, copy, modify, and/or 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.
*/
//==============================================================================
#if ENABLE_SOCI_POSTGRESQL
#if BEAST_INCLUDE_BEASTCONFIG
#include <BeastConfig.h>
#endif
#include <algorithm>
#if BEAST_MSVC
#define SOCI_LIB_PREFIX ""
#define SOCI_LIB_SUFFIX ".dll"
#else
#define SOCI_LIB_PREFIX "lib"
#define SOCI_LIB_SUFFIX ".so"
#endif
#include<backends/postgresql/blob.cpp>
#include<backends/postgresql/common.cpp>
#include<backends/postgresql/error.cpp>
#include<backends/postgresql/factory.cpp>
#include<backends/postgresql/row-id.cpp>
#include<backends/postgresql/session.cpp>
#include<backends/postgresql/standard-into-type.cpp>
#include<backends/postgresql/standard-use-type.cpp>
#include<backends/postgresql/statement.cpp>
#include<backends/postgresql/vector-into-type.cpp>
#include<backends/postgresql/vector-use-type.cpp>
#endif // ENABLE_SOCI_POSTGRESQL

View File

@@ -38,9 +38,8 @@ StoreSqdb::~StoreSqdb ()
void
StoreSqdb::open (SociConfig const& sociConfig)
{
sociConfig.open (m_session);
m_journal.info << "Opening " << sociConfig.connectionString ();
sociConfig.open (m_session);
}
}

View File

@@ -33,7 +33,7 @@ struct type_conversion<unsigned char>
const base_type min = (std::numeric_limits<unsigned char>::min)();
if (in < min || in > max)
{
throw soci_error("Value outside of allowed range.");
/* throw soci_error("Value outside of allowed range."); */
}
out = static_cast<unsigned char>(in);
@@ -64,7 +64,7 @@ struct type_conversion<unsigned short>
const long long min = (std::numeric_limits<unsigned short>::min)();
if (in < min || in > max)
{
throw soci_error("Value outside of allowed range.");
/* throw soci_error("Value outside of allowed range."); */
}
out = static_cast<unsigned short>(in);
@@ -95,7 +95,7 @@ struct type_conversion<unsigned int>
const long long min = (std::numeric_limits<unsigned int>::min)();
if (in < min || in > max)
{
throw soci_error("Value outside of allowed range.");
/* throw soci_error("Value outside of allowed range."); */
}
out = static_cast<unsigned int>(in);