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