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  DBConfig 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  DBConfig 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  DBConfig 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  // SOCI requires boost::optional (not std::optional) as
227  // parameters.
228  boost::optional<std::int32_t> ig;
229  // Known bug: https://github.com/SOCI/soci/issues/926
230  // boost::optional<std::uint32_t> uig;
231  uint32_t uig = 0;
232  boost::optional<std::int64_t> big;
233  boost::optional<std::uint64_t> ubig;
234  s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig),
235  soci::into(uig), soci::into(big), soci::into(ubig);
236  BEAST_EXPECT(
237  *ig == id[0] && uig == uid[0] && *big == bid[0] &&
238  *ubig == ubid[0]);
239  }
240  catch (std::exception&)
241  {
242  fail();
243  }
244  // There are too many issues when working with soci::row and
245  // boost::tuple. DO NOT USE soci row! I had a set of workarounds to
246  // make soci row less error prone, I'm keeping these tests in case I
247  // try to add soci::row and boost::tuple back into soci.
248 #if 0
249  try
250  {
251  std::int32_t ig = 0;
252  std::uint32_t uig = 0;
253  std::int64_t big = 0;
254  std::uint64_t ubig = 0;
255  soci::row r;
256  s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
257  ig = r.get<std::int32_t>(0);
258  uig = r.get<std::uint32_t>(1);
259  big = r.get<std::int64_t>(2);
260  ubig = r.get<std::uint64_t>(3);
261  BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
262  ubig == ubid[0]);
263  }
264  catch (std::exception&)
265  {
266  fail ();
267  }
268  try
269  {
270  std::int32_t ig = 0;
271  std::uint32_t uig = 0;
272  std::int64_t big = 0;
273  std::uint64_t ubig = 0;
274  soci::row r;
275  s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
276  ig = r.get<std::int32_t>("I");
277  uig = r.get<std::uint32_t>("UI");
278  big = r.get<std::int64_t>("BI");
279  ubig = r.get<std::uint64_t>("UBI");
280  BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
281  ubig == ubid[0]);
282  }
283  catch (std::exception&)
284  {
285  fail ();
286  }
287  try
288  {
289  boost::tuple<std::int32_t,
291  std::int64_t,
292  std::uint64_t> d;
293  s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
294  BEAST_EXPECT(get<0>(d) == id[0] && get<1>(d) == uid[0] &&
295  get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
296  }
297  catch (std::exception&)
298  {
299  fail ();
300  }
301 #endif
302  }
303  {
304  namespace bfs = boost::filesystem;
305  // Remove the database
306  bfs::path dbPath(sc.connectionString());
307  if (bfs::is_regular_file(dbPath))
308  bfs::remove(dbPath);
309  }
310  }
311  void
313  {
314  testcase("deleteWithSubselect");
315  BasicConfig c;
317  DBConfig sc(c, "SociTestDB");
318  {
319  soci::session s;
320  sc.open(s);
321  const char* dbInit[] = {
322  "BEGIN TRANSACTION;",
323  "CREATE TABLE Ledgers ( \
324  LedgerHash CHARACTER(64) PRIMARY KEY, \
325  LedgerSeq BIGINT UNSIGNED \
326  );",
327  "CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);"};
328  for (auto const c : dbInit)
329  s << c;
330  char lh[65];
331  memset(lh, 'a', 64);
332  lh[64] = '\0';
333  int toIncIndex = 63;
334  int const numRows = 16;
335  std::vector<std::string> ledgerHashes;
336  std::vector<int> ledgerIndexes;
337  ledgerHashes.reserve(numRows);
338  ledgerIndexes.reserve(numRows);
339  for (int i = 0; i < numRows; ++i)
340  {
341  ++lh[toIncIndex];
342  if (lh[toIncIndex] == 'z')
343  --toIncIndex;
344  ledgerHashes.emplace_back(lh);
345  ledgerIndexes.emplace_back(i);
346  }
347  s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
348  "(:lh, :li);",
349  soci::use(ledgerHashes), soci::use(ledgerIndexes);
350 
351  std::vector<int> ledgersLS(numRows * 2);
352  s << "SELECT LedgerSeq FROM Ledgers;", soci::into(ledgersLS);
353  BEAST_EXPECT(ledgersLS.size() == numRows);
354  }
355  namespace bfs = boost::filesystem;
356  // Remove the database
357  bfs::path dbPath(sc.connectionString());
358  if (bfs::is_regular_file(dbPath))
359  bfs::remove(dbPath);
360  }
361  void
362  run() override
363  {
368  }
369 };
370 
371 BEAST_DEFINE_TESTSUITE(SociDB, core, ripple);
372 
373 } // namespace ripple
ripple::BEAST_DEFINE_TESTSUITE
BEAST_DEFINE_TESTSUITE(AccountTxPaging, app, ripple)
std::exception
STL class.
ripple::SociDB_test::testSQLiteDeleteWithSubselect
void testSQLiteDeleteWithSubselect()
Definition: SociDB_test.cpp:312
ripple::DBConfig::connectionString
std::string connectionString() const
Definition: SociDB.cpp:86
ripple::SociDB_test::run
void run() override
Definition: SociDB_test.cpp:362
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
ripple::DBConfig::open
void open(soci::session &s) const
Definition: SociDB.cpp:92
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
ripple::DBConfig
DBConfig is used when a client wants to delay opening a soci::session after parsing the config parame...
Definition: SociDB.h:57
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:164
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:143
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
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:215