xrpld
Loading...
Searching...
No Matches
SociDB_test.cpp
1#include <test/jtx/TestSuite.h>
2
3#include <xrpl/basics/contract.h>
4#include <xrpl/beast/unit_test/suite.h>
5#include <xrpl/config/BasicConfig.h>
6#include <xrpl/config/Constants.h>
7#include <xrpl/rdb/SociDB.h>
8
9#include <boost/optional/optional.hpp> // IWYU pragma: keep
10
11#include <soci/boost-optional.h> // IWYU pragma: keep
12#include <soci/into.h>
13#include <soci/session.h>
14#include <soci/use.h>
15
16#include <algorithm>
17#include <cstdint>
18#include <cstring>
19#include <exception>
20#include <filesystem>
21#include <iterator>
22#include <limits>
23#include <stdexcept>
24#include <string_view>
25#include <utility>
26#include <vector>
27
28namespace xrpl {
29class SociDB_test final : public TestSuite
30{
31private:
32 static void
34 {
35 config.overwrite(Sections::kSqdb, Keys::kBackend, "sqlite");
36 auto value = dbPath.string();
37 if (!value.empty())
38 config.legacy(Sections::kDatabasePath, value);
39 }
40
41 static void
43 {
44 using namespace std::filesystem;
45 if (!exists(dbPath) || !is_directory(dbPath) || !is_empty(dbPath))
46 return;
47 remove(dbPath);
48 }
49
50 static void
52 {
53 using namespace std::filesystem;
54 if (!exists(dbPath))
55 {
56 create_directory(dbPath);
57 return;
58 }
59
60 if (!is_directory(dbPath))
61 {
62 // someone created a file where we want to put out directory
63 Throw<std::runtime_error>("Cannot create directory: " + dbPath.string());
64 }
65 }
68 {
69 return std::filesystem::current_path() / "socidb_test_databases";
70 }
71
72public:
74 {
75 try
76 {
78 }
79 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
80 {
81 }
82 }
83 ~SociDB_test() override
84 {
85 try
86 {
88 }
89 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
90 {
91 }
92 }
93 void
95 {
96 // confirm that files are given the correct extensions
97 testcase("sqliteFileNames");
101 {{"peerfinder", ".sqlite"},
102 {"state", ".db"},
103 {"random", ".db"},
104 {"validators", ".sqlite"}});
105
106 for (auto const& i : d)
107 {
108 DBConfig const sc(c, i.first);
109 BEAST_EXPECT(sc.connectionString().ends_with(i.first + i.second));
110 }
111 }
112 void
114 {
115 testcase("open");
116 BasicConfig c;
118 DBConfig const sc(c, "SociTestDB");
119 std::vector<std::string> const stringData({"String1", "String2", "String3"});
120 std::vector<int> const intData({1, 2, 3});
121 auto checkValues = [this, &stringData, &intData](soci::session& s) {
122 // Check values in db
123 std::vector<std::string> stringResult(20 * stringData.size());
124 std::vector<int> intResult(20 * intData.size());
125 s << "SELECT StringData, IntData FROM SociTestTable;", soci::into(stringResult),
126 soci::into(intResult);
127 BEAST_EXPECT(
128 stringResult.size() == stringData.size() && intResult.size() == intData.size());
129 for (int i = 0; i < stringResult.size(); ++i)
130 {
131 auto si = std::distance(
132 stringData.begin(), std::ranges::find(stringData, stringResult[i]));
133 auto ii = std::distance(intData.begin(), std::ranges::find(intData, intResult[i]));
134 BEAST_EXPECT(si == ii && si < stringResult.size());
135 }
136 };
137
138 {
139 soci::session s;
140 sc.open(s);
141 s << "CREATE TABLE IF NOT EXISTS SociTestTable ("
142 " Key INTEGER PRIMARY KEY,"
143 " StringData TEXT,"
144 " IntData INTEGER"
145 ");";
146
147 s << "INSERT INTO SociTestTable (StringData, IntData) VALUES "
148 "(:stringData, :intData);",
149 soci::use(stringData), soci::use(intData);
150 checkValues(s);
151 }
152 {
153 // Check values in db after session was closed
154 soci::session s;
155 sc.open(s);
156 checkValues(s);
157 }
158 {
159 namespace bfs = std::filesystem;
160 // Remove the database
161 bfs::path const dbPath(sc.connectionString());
162 if (bfs::is_regular_file(dbPath))
163 bfs::remove(dbPath);
164 }
165 }
166
167 void
169 {
170 testcase("select");
171 BasicConfig c;
173 DBConfig const sc(c, "SociTestDB");
176 std::vector<std::int64_t> const bid({-10, -20, -30});
178 std::vector<std::int32_t> const id({-1, -2, -3});
179
180 {
181 soci::session s;
182 sc.open(s);
183
184 s << "DROP TABLE IF EXISTS STT;";
185
186 s << "CREATE TABLE STT ("
187 " I INTEGER,"
188 " UI INTEGER UNSIGNED,"
189 " BI BIGINT,"
190 " UBI BIGINT UNSIGNED"
191 ");";
192
193 s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
194 "(:id, :idu, :bid, :bidu);",
195 soci::use(id), soci::use(uid), soci::use(bid), soci::use(ubid);
196
197 try
198 {
199 std::int32_t ig = 0;
200 std::uint32_t uig = 0;
201 std::int64_t big = 0;
202 std::uint64_t ubig = 0;
203 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig),
204 soci::into(big), soci::into(ubig);
205 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] && ubig == ubid[0]);
206 }
207 catch (std::exception&)
208 {
209 fail();
210 }
211 try
212 {
213 // SOCI requires boost::optional (not std::optional) as
214 // parameters.
215 boost::optional<std::int32_t> ig;
216 // Known bug: https://github.com/SOCI/soci/issues/926
217 // boost::optional<std::uint32_t> uig;
218 uint32_t uig = 0;
219 boost::optional<std::int64_t> big;
220 boost::optional<std::uint64_t> ubig;
221 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig),
222 soci::into(big), soci::into(ubig);
223 BEAST_EXPECT(*ig == id[0] && uig == uid[0] && *big == bid[0] && *ubig == ubid[0]);
224 }
225 catch (std::exception&)
226 {
227 fail();
228 }
229 // There are too many issues when working with soci::row and
230 // boost::tuple. DO NOT USE soci row!
231 }
232 {
233 namespace bfs = std::filesystem;
234 // Remove the database
235 bfs::path const dbPath(sc.connectionString());
236 if (bfs::is_regular_file(dbPath))
237 bfs::remove(dbPath);
238 }
239 }
240 void
242 {
243 testcase("deleteWithSubselect");
244 BasicConfig c;
246 DBConfig const sc(c, "SociTestDB");
247 {
248 soci::session s;
249 sc.open(s);
250
251 std::string_view const dbInit[] = {
252 "BEGIN TRANSACTION;",
253 "CREATE TABLE Ledgers ( \
254 LedgerHash CHARACTER(64) PRIMARY KEY, \
255 LedgerSeq BIGINT UNSIGNED \
256 );",
257 "CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);"};
258 for (auto const c : dbInit)
259 s << c;
260 char lh[65];
261 memset(lh, 'a', 64);
262 lh[64] = '\0';
263 int toIncIndex = 63;
264 int const numRows = 16;
265 std::vector<std::string> ledgerHashes;
266 std::vector<int> ledgerIndexes;
267 ledgerHashes.reserve(numRows);
268 ledgerIndexes.reserve(numRows);
269 for (int i = 0; i < numRows; ++i)
270 {
271 ++lh[toIncIndex];
272 if (lh[toIncIndex] == 'z')
273 --toIncIndex;
274 ledgerHashes.emplace_back(lh);
275 ledgerIndexes.emplace_back(i);
276 }
277 s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
278 "(:lh, :li);",
279 soci::use(ledgerHashes), soci::use(ledgerIndexes);
280
281 std::vector<int> ledgersLS(numRows * 2);
282 s << "SELECT LedgerSeq FROM Ledgers;", soci::into(ledgersLS);
283 BEAST_EXPECT(ledgersLS.size() == numRows);
284 }
285 namespace bfs = std::filesystem;
286 // Remove the database
287 bfs::path const dbPath(sc.connectionString());
288 if (bfs::is_regular_file(dbPath))
289 bfs::remove(dbPath);
290 }
291 void
299};
300
302
303} // namespace xrpl
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:554
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:155
Holds unparsed configuration information.
void overwrite(std::string const &section, std::string const &key, std::string const &value)
Overwrite a key/value pair with a command line argument If the section does not exist it is created.
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
DBConfig is used when a client wants to delay opening a soci::session after parsing the config parame...
Definition SociDB.h:44
void open(soci::session &s) const
Definition SociDB.cpp:84
std::string connectionString() const
Definition SociDB.cpp:78
static void setupDatabaseDir(std::filesystem::path const &dbPath)
static void cleanupDatabaseDir(std::filesystem::path const &dbPath)
static std::filesystem::path getDatabasePath()
void testSQLiteDeleteWithSubselect()
static void setupSQLiteConfig(BasicConfig &config, std::filesystem::path const &dbPath)
~SociDB_test() override
void run() override
Runs the suite.
T create_directory(T... args)
T current_path(T... args)
T distance(T... args)
T emplace_back(T... args)
T ends_with(T... args)
T exists(T... args)
T find(T... args)
T is_directory(T... args)
T is_empty(T... args)
T max(T... args)
T memset(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:52
T remove(T... args)
T reserve(T... args)
T size(T... args)
static constexpr auto kBackend
Definition Constants.h:93
static constexpr auto kSqdb
Definition Constants.h:60
static constexpr auto kDatabasePath
Definition Constants.h:13