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