rippled
Loading...
Searching...
No Matches
SociDB_test.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2015 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <test/jtx/TestSuite.h>
21
22#include <xrpld/core/SociDB.h>
23
24#include <xrpl/basics/BasicConfig.h>
25#include <xrpl/basics/contract.h>
26
27#include <boost/algorithm/string.hpp>
28#include <boost/filesystem.hpp>
29
30namespace ripple {
31class SociDB_test final : public TestSuite
32{
33private:
34 static void
36 BasicConfig& config,
37 boost::filesystem::path const& dbPath)
38 {
39 config.overwrite("sqdb", "backend", "sqlite");
40 auto value = dbPath.string();
41 if (!value.empty())
42 config.legacy("database_path", value);
43 }
44
45 static void
46 cleanupDatabaseDir(boost::filesystem::path const& dbPath)
47 {
48 using namespace boost::filesystem;
49 if (!exists(dbPath) || !is_directory(dbPath) || !is_empty(dbPath))
50 return;
51 remove(dbPath);
52 }
53
54 static void
55 setupDatabaseDir(boost::filesystem::path const& dbPath)
56 {
57 using namespace boost::filesystem;
58 if (!exists(dbPath))
59 {
60 create_directory(dbPath);
61 return;
62 }
63
64 if (!is_directory(dbPath))
65 {
66 // someone created a file where we want to put out directory
67 Throw<std::runtime_error>(
68 "Cannot create directory: " + dbPath.string());
69 }
70 }
71 static boost::filesystem::path
73 {
74 return boost::filesystem::current_path() / "socidb_test_databases";
75 }
76
77public:
79 {
80 try
81 {
83 }
84 catch (std::exception const&)
85 {
86 }
87 }
89 {
90 try
91 {
93 }
94 catch (std::exception const&)
95 {
96 }
97 }
98 void
100 {
101 // confirm that files are given the correct exensions
102 testcase("sqliteFileNames");
103 BasicConfig c;
106 {{"peerfinder", ".sqlite"},
107 {"state", ".db"},
108 {"random", ".db"},
109 {"validators", ".sqlite"}});
110
111 for (auto const& i : d)
112 {
113 DBConfig sc(c, i.first);
114 BEAST_EXPECT(
115 boost::ends_with(sc.connectionString(), i.first + i.second));
116 }
117 }
118 void
120 {
121 testcase("open");
122 BasicConfig c;
124 DBConfig sc(c, "SociTestDB");
125 std::vector<std::string> const stringData(
126 {"String1", "String2", "String3"});
127 std::vector<int> const intData({1, 2, 3});
128 auto checkValues = [this, &stringData, &intData](soci::session& s) {
129 // Check values in db
130 std::vector<std::string> stringResult(20 * stringData.size());
131 std::vector<int> intResult(20 * intData.size());
132 s << "SELECT StringData, IntData FROM SociTestTable;",
133 soci::into(stringResult), soci::into(intResult);
134 BEAST_EXPECT(
135 stringResult.size() == stringData.size() &&
136 intResult.size() == intData.size());
137 for (int i = 0; i < stringResult.size(); ++i)
138 {
139 auto si = std::distance(
140 stringData.begin(),
141 std::find(
142 stringData.begin(), stringData.end(), stringResult[i]));
143 auto ii = std::distance(
144 intData.begin(),
145 std::find(intData.begin(), intData.end(), intResult[i]));
146 BEAST_EXPECT(si == ii && si < stringResult.size());
147 }
148 };
149
150 {
151 soci::session s;
152 sc.open(s);
153 s << "CREATE TABLE IF NOT EXISTS SociTestTable ("
154 " Key INTEGER PRIMARY KEY,"
155 " StringData TEXT,"
156 " IntData INTEGER"
157 ");";
158
159 s << "INSERT INTO SociTestTable (StringData, IntData) VALUES "
160 "(:stringData, :intData);",
161 soci::use(stringData), soci::use(intData);
162 checkValues(s);
163 }
164 {
165 // Check values in db after session was closed
166 soci::session s;
167 sc.open(s);
168 checkValues(s);
169 }
170 {
171 namespace bfs = boost::filesystem;
172 // Remove the database
173 bfs::path dbPath(sc.connectionString());
174 if (bfs::is_regular_file(dbPath))
175 bfs::remove(dbPath);
176 }
177 }
178
179 void
181 {
182 testcase("select");
183 BasicConfig c;
185 DBConfig sc(c, "SociTestDB");
188 std::vector<std::int64_t> const bid({-10, -20, -30});
191 std::vector<std::int32_t> const id({-1, -2, -3});
192
193 {
194 soci::session s;
195 sc.open(s);
196
197 s << "DROP TABLE IF EXISTS STT;";
198
199 s << "CREATE TABLE STT ("
200 " I INTEGER,"
201 " UI INTEGER UNSIGNED,"
202 " BI BIGINT,"
203 " UBI BIGINT UNSIGNED"
204 ");";
205
206 s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
207 "(:id, :idu, :bid, :bidu);",
208 soci::use(id), soci::use(uid), soci::use(bid), soci::use(ubid);
209
210 try
211 {
212 std::int32_t ig = 0;
213 std::uint32_t uig = 0;
214 std::int64_t big = 0;
215 std::uint64_t ubig = 0;
216 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig),
217 soci::into(uig), soci::into(big), soci::into(ubig);
218 BEAST_EXPECT(
219 ig == id[0] && uig == uid[0] && big == bid[0] &&
220 ubig == ubid[0]);
221 }
222 catch (std::exception&)
223 {
224 fail();
225 }
226 try
227 {
228 // SOCI requires boost::optional (not std::optional) as
229 // parameters.
230 boost::optional<std::int32_t> ig;
231 // Known bug: https://github.com/SOCI/soci/issues/926
232 // boost::optional<std::uint32_t> uig;
233 uint32_t uig = 0;
234 boost::optional<std::int64_t> big;
235 boost::optional<std::uint64_t> ubig;
236 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig),
237 soci::into(uig), soci::into(big), soci::into(ubig);
238 BEAST_EXPECT(
239 *ig == id[0] && uig == uid[0] && *big == bid[0] &&
240 *ubig == ubid[0]);
241 }
242 catch (std::exception&)
243 {
244 fail();
245 }
246 // There are too many issues when working with soci::row and
247 // boost::tuple. DO NOT USE soci row! I had a set of workarounds to
248 // make soci row less error prone, I'm keeping these tests in case I
249 // try to add soci::row and boost::tuple back into soci.
250#if 0
251 try
252 {
253 std::int32_t ig = 0;
254 std::uint32_t uig = 0;
255 std::int64_t big = 0;
256 std::uint64_t ubig = 0;
257 soci::row r;
258 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
259 ig = r.get<std::int32_t>(0);
260 uig = r.get<std::uint32_t>(1);
261 big = r.get<std::int64_t>(2);
262 ubig = r.get<std::uint64_t>(3);
263 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
264 ubig == ubid[0]);
265 }
266 catch (std::exception&)
267 {
268 fail ();
269 }
270 try
271 {
272 std::int32_t ig = 0;
273 std::uint32_t uig = 0;
274 std::int64_t big = 0;
275 std::uint64_t ubig = 0;
276 soci::row r;
277 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
278 ig = r.get<std::int32_t>("I");
279 uig = r.get<std::uint32_t>("UI");
280 big = r.get<std::int64_t>("BI");
281 ubig = r.get<std::uint64_t>("UBI");
282 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
283 ubig == ubid[0]);
284 }
285 catch (std::exception&)
286 {
287 fail ();
288 }
289 try
290 {
291 boost::tuple<std::int32_t,
294 std::uint64_t> d;
295 s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
296 BEAST_EXPECT(get<0>(d) == id[0] && get<1>(d) == uid[0] &&
297 get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
298 }
299 catch (std::exception&)
300 {
301 fail ();
302 }
303#endif
304 }
305 {
306 namespace bfs = boost::filesystem;
307 // Remove the database
308 bfs::path dbPath(sc.connectionString());
309 if (bfs::is_regular_file(dbPath))
310 bfs::remove(dbPath);
311 }
312 }
313 void
315 {
316 testcase("deleteWithSubselect");
317 BasicConfig c;
319 DBConfig sc(c, "SociTestDB");
320 {
321 soci::session s;
322 sc.open(s);
323 char const* dbInit[] = {
324 "BEGIN TRANSACTION;",
325 "CREATE TABLE Ledgers ( \
326 LedgerHash CHARACTER(64) PRIMARY KEY, \
327 LedgerSeq BIGINT UNSIGNED \
328 );",
329 "CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);"};
330 for (auto const c : dbInit)
331 s << c;
332 char lh[65];
333 memset(lh, 'a', 64);
334 lh[64] = '\0';
335 int toIncIndex = 63;
336 int const numRows = 16;
337 std::vector<std::string> ledgerHashes;
338 std::vector<int> ledgerIndexes;
339 ledgerHashes.reserve(numRows);
340 ledgerIndexes.reserve(numRows);
341 for (int i = 0; i < numRows; ++i)
342 {
343 ++lh[toIncIndex];
344 if (lh[toIncIndex] == 'z')
345 --toIncIndex;
346 ledgerHashes.emplace_back(lh);
347 ledgerIndexes.emplace_back(i);
348 }
349 s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
350 "(:lh, :li);",
351 soci::use(ledgerHashes), soci::use(ledgerIndexes);
352
353 std::vector<int> ledgersLS(numRows * 2);
354 s << "SELECT LedgerSeq FROM Ledgers;", soci::into(ledgersLS);
355 BEAST_EXPECT(ledgersLS.size() == numRows);
356 }
357 namespace bfs = boost::filesystem;
358 // Remove the database
359 bfs::path dbPath(sc.connectionString());
360 if (bfs::is_regular_file(dbPath))
361 bfs::remove(dbPath);
362 }
363 void
371};
372
373BEAST_DEFINE_TESTSUITE(SociDB, core, ripple);
374
375} // namespace ripple
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:155
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:533
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:61
void open(soci::session &s) const
Definition SociDB.cpp:95
std::string connectionString() const
Definition SociDB.cpp:89
static void setupDatabaseDir(boost::filesystem::path const &dbPath)
static void setupSQLiteConfig(BasicConfig &config, boost::filesystem::path const &dbPath)
void run() override
Runs the suite.
static boost::filesystem::path getDatabasePath()
static void cleanupDatabaseDir(boost::filesystem::path const &dbPath)
void testSQLiteDeleteWithSubselect()
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:25
T reserve(T... args)
T size(T... args)