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/algorithm/string/predicate.hpp>
10#include <boost/filesystem/operations.hpp>
11#include <boost/filesystem/path.hpp>
12#include <boost/optional/optional.hpp> // IWYU pragma: keep
13
14#include <soci/into.h>
15#include <soci/session.h>
16#include <soci/use.h>
17
18#include <algorithm>
19#include <cstdint>
20#include <cstring>
21#include <exception>
22#include <iterator>
23#include <limits>
24#include <stdexcept>
25#include <string_view>
26#include <utility>
27#include <vector>
28
29namespace xrpl {
30class SociDB_test final : public TestSuite
31{
32private:
33 static void
34 setupSQLiteConfig(BasicConfig& config, boost::filesystem::path const& dbPath)
35 {
36 config.overwrite(Sections::kSqdb, Keys::kBackend, "sqlite");
37 auto value = dbPath.string();
38 if (!value.empty())
39 config.legacy(Sections::kDatabasePath, value);
40 }
41
42 static void
43 cleanupDatabaseDir(boost::filesystem::path const& dbPath)
44 {
45 using namespace boost::filesystem;
46 if (!exists(dbPath) || !is_directory(dbPath) || !is_empty(dbPath))
47 return;
48 remove(dbPath);
49 }
50
51 static void
52 setupDatabaseDir(boost::filesystem::path const& dbPath)
53 {
54 using namespace boost::filesystem;
55 if (!exists(dbPath))
56 {
57 create_directory(dbPath);
58 return;
59 }
60
61 if (!is_directory(dbPath))
62 {
63 // someone created a file where we want to put out directory
64 Throw<std::runtime_error>("Cannot create directory: " + dbPath.string());
65 }
66 }
67 static boost::filesystem::path
69 {
70 return boost::filesystem::current_path() / "socidb_test_databases";
71 }
72
73public:
75 {
76 try
77 {
79 }
80 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
81 {
82 }
83 }
84 ~SociDB_test() override
85 {
86 try
87 {
89 }
90 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
91 {
92 }
93 }
94 void
96 {
97 // confirm that files are given the correct extensions
98 testcase("sqliteFileNames");
102 {{"peerfinder", ".sqlite"},
103 {"state", ".db"},
104 {"random", ".db"},
105 {"validators", ".sqlite"}});
106
107 for (auto const& i : d)
108 {
109 DBConfig const sc(c, i.first);
110 BEAST_EXPECT(boost::ends_with(sc.connectionString(), i.first + i.second));
111 }
112 }
113 void
115 {
116 testcase("open");
117 BasicConfig c;
119 DBConfig const sc(c, "SociTestDB");
120 std::vector<std::string> const stringData({"String1", "String2", "String3"});
121 std::vector<int> const intData({1, 2, 3});
122 auto checkValues = [this, &stringData, &intData](soci::session& s) {
123 // Check values in db
124 std::vector<std::string> stringResult(20 * stringData.size());
125 std::vector<int> intResult(20 * intData.size());
126 s << "SELECT StringData, IntData FROM SociTestTable;", soci::into(stringResult),
127 soci::into(intResult);
128 BEAST_EXPECT(
129 stringResult.size() == stringData.size() && intResult.size() == intData.size());
130 for (int i = 0; i < stringResult.size(); ++i)
131 {
132 auto si = std::distance(
133 stringData.begin(), std::ranges::find(stringData, stringResult[i]));
134 auto ii = std::distance(intData.begin(), std::ranges::find(intData, intResult[i]));
135 BEAST_EXPECT(si == ii && si < stringResult.size());
136 }
137 };
138
139 {
140 soci::session s;
141 sc.open(s);
142 s << "CREATE TABLE IF NOT EXISTS SociTestTable ("
143 " Key INTEGER PRIMARY KEY,"
144 " StringData TEXT,"
145 " IntData INTEGER"
146 ");";
147
148 s << "INSERT INTO SociTestTable (StringData, IntData) VALUES "
149 "(:stringData, :intData);",
150 soci::use(stringData), soci::use(intData);
151 checkValues(s);
152 }
153 {
154 // Check values in db after session was closed
155 soci::session s;
156 sc.open(s);
157 checkValues(s);
158 }
159 {
160 namespace bfs = boost::filesystem;
161 // Remove the database
162 bfs::path const dbPath(sc.connectionString());
163 if (bfs::is_regular_file(dbPath))
164 bfs::remove(dbPath);
165 }
166 }
167
168 void
170 {
171 testcase("select");
172 BasicConfig c;
174 DBConfig const sc(c, "SociTestDB");
177 std::vector<std::int64_t> const bid({-10, -20, -30});
179 std::vector<std::int32_t> const id({-1, -2, -3});
180
181 {
182 soci::session s;
183 sc.open(s);
184
185 s << "DROP TABLE IF EXISTS STT;";
186
187 s << "CREATE TABLE STT ("
188 " I INTEGER,"
189 " UI INTEGER UNSIGNED,"
190 " BI BIGINT,"
191 " UBI BIGINT UNSIGNED"
192 ");";
193
194 s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
195 "(:id, :idu, :bid, :bidu);",
196 soci::use(id), soci::use(uid), soci::use(bid), soci::use(ubid);
197
198 try
199 {
200 std::int32_t ig = 0;
201 std::uint32_t uig = 0;
202 std::int64_t big = 0;
203 std::uint64_t ubig = 0;
204 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig),
205 soci::into(big), soci::into(ubig);
206 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] && ubig == ubid[0]);
207 }
208 catch (std::exception&)
209 {
210 fail();
211 }
212 try
213 {
214 // SOCI requires boost::optional (not std::optional) as
215 // parameters.
216 boost::optional<std::int32_t> ig;
217 // Known bug: https://github.com/SOCI/soci/issues/926
218 // boost::optional<std::uint32_t> uig;
219 uint32_t uig = 0;
220 boost::optional<std::int64_t> big;
221 boost::optional<std::uint64_t> ubig;
222 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig), soci::into(uig),
223 soci::into(big), soci::into(ubig);
224 BEAST_EXPECT(*ig == id[0] && uig == uid[0] && *big == bid[0] && *ubig == ubid[0]);
225 }
226 catch (std::exception&)
227 {
228 fail();
229 }
230 // There are too many issues when working with soci::row and
231 // boost::tuple. DO NOT USE soci row! I had a set of workarounds to
232 // make soci row less error prone, I'm keeping these tests in case I
233 // try to add soci::row and boost::tuple back into soci.
234#if 0
235 try
236 {
237 std::int32_t ig = 0;
238 std::uint32_t uig = 0;
239 std::int64_t big = 0;
240 std::uint64_t ubig = 0;
241 soci::row r;
242 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
243 ig = r.get<std::int32_t>(0);
244 uig = r.get<std::uint32_t>(1);
245 big = r.get<std::int64_t>(2);
246 ubig = r.get<std::uint64_t>(3);
247 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
248 ubig == ubid[0]);
249 }
250 catch (std::exception&)
251 {
252 fail ();
253 }
254 try
255 {
256 std::int32_t ig = 0;
257 std::uint32_t uig = 0;
258 std::int64_t big = 0;
259 std::uint64_t ubig = 0;
260 soci::row r;
261 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
262 ig = r.get<std::int32_t>("I");
263 uig = r.get<std::uint32_t>("UI");
264 big = r.get<std::int64_t>("BI");
265 ubig = r.get<std::uint64_t>("UBI");
266 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
267 ubig == ubid[0]);
268 }
269 catch (std::exception&)
270 {
271 fail ();
272 }
273 try
274 {
275 boost::tuple<std::int32_t,
278 std::uint64_t> d;
279 s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
280 BEAST_EXPECT(get<0>(d) == id[0] && get<1>(d) == uid[0] &&
281 get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
282 }
283 catch (std::exception&)
284 {
285 fail ();
286 }
287#endif
288 }
289 {
290 namespace bfs = boost::filesystem;
291 // Remove the database
292 bfs::path const dbPath(sc.connectionString());
293 if (bfs::is_regular_file(dbPath))
294 bfs::remove(dbPath);
295 }
296 }
297 void
299 {
300 testcase("deleteWithSubselect");
301 BasicConfig c;
303 DBConfig const sc(c, "SociTestDB");
304 {
305 soci::session s;
306 sc.open(s);
307
308 std::string_view const dbInit[] = {
309 "BEGIN TRANSACTION;",
310 "CREATE TABLE Ledgers ( \
311 LedgerHash CHARACTER(64) PRIMARY KEY, \
312 LedgerSeq BIGINT UNSIGNED \
313 );",
314 "CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);"};
315 for (auto const c : dbInit)
316 s << c;
317 char lh[65];
318 memset(lh, 'a', 64);
319 lh[64] = '\0';
320 int toIncIndex = 63;
321 int const numRows = 16;
322 std::vector<std::string> ledgerHashes;
323 std::vector<int> ledgerIndexes;
324 ledgerHashes.reserve(numRows);
325 ledgerIndexes.reserve(numRows);
326 for (int i = 0; i < numRows; ++i)
327 {
328 ++lh[toIncIndex];
329 if (lh[toIncIndex] == 'z')
330 --toIncIndex;
331 ledgerHashes.emplace_back(lh);
332 ledgerIndexes.emplace_back(i);
333 }
334 s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
335 "(:lh, :li);",
336 soci::use(ledgerHashes), soci::use(ledgerIndexes);
337
338 std::vector<int> ledgersLS(numRows * 2);
339 s << "SELECT LedgerSeq FROM Ledgers;", soci::into(ledgersLS);
340 BEAST_EXPECT(ledgersLS.size() == numRows);
341 }
342 namespace bfs = boost::filesystem;
343 // Remove the database
344 bfs::path const dbPath(sc.connectionString());
345 if (bfs::is_regular_file(dbPath))
346 bfs::remove(dbPath);
347 }
348 void
356};
357
359
360} // namespace xrpl
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:522
TestcaseT testcase
Memberspace for declaring test cases.
Definition suite.h:149
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:40
void open(soci::session &s) const
Definition SociDB.cpp:86
std::string connectionString() const
Definition SociDB.cpp:80
static void setupDatabaseDir(boost::filesystem::path const &dbPath)
static boost::filesystem::path getDatabasePath()
static void setupSQLiteConfig(BasicConfig &config, boost::filesystem::path const &dbPath)
void testSQLiteDeleteWithSubselect()
~SociDB_test() override
void run() override
Runs the suite.
static void cleanupDatabaseDir(boost::filesystem::path const &dbPath)
T distance(T... args)
T emplace_back(T... args)
T find(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, xrpl)
XRPL_NO_SANITIZE_ADDRESS void Throw(Args &&... args)
Definition contract.h:49
T reserve(T... args)
T size(T... args)
static constexpr auto kBackend
Definition Constants.h:92
static constexpr auto kSqdb
Definition Constants.h:59
static constexpr auto kDatabasePath
Definition Constants.h:13