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