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