Electroneum
lmdb.cpp
Go to the documentation of this file.
1 // Copyright (c) 2014-2018, The Monero Project
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #include <boost/range/algorithm_ext/iota.hpp>
30 #include <boost/range/algorithm/equal.hpp>
31 #include <gtest/gtest.h>
32 
33 #include "lmdb/database.h"
34 #include "lmdb/table.h"
35 #include "lmdb/transaction.h"
36 #include "lmdb/util.h"
37 
38 namespace
39 {
40  enum class choice : unsigned {};
41  enum class big_choice : unsigned long {};
42 
43  struct bytes {
44  char data[16];
45  };
46 
47  ELECTRONEUM_CURSOR(test_cursor);
48 
49  template<typename T>
50  int run_compare(T left, T right, MDB_cmp_func* cmp)
51  {
52  MDB_val left_val = lmdb::to_val(left);
53  MDB_val right_val = lmdb::to_val(right);
54  return (*cmp)(&left_val, &right_val);
55  }
56 }
57 
58 TEST(LMDB, Traits)
59 {
60  EXPECT_TRUE((std::is_same<void, lmdb::identity<void>::type>()));
61  EXPECT_TRUE((std::is_same<unsigned, lmdb::identity<unsigned>::type>()));
62 
63  EXPECT_TRUE((std::is_same<void, lmdb::native_type<void>>()));
64  EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<unsigned>>()));
65  EXPECT_TRUE((std::is_same<unsigned, lmdb::native_type<choice>>()));
66  EXPECT_TRUE((std::is_same<unsigned long, lmdb::native_type<big_choice>>()));
67 }
68 
69 TEST(LMDB, ToNative)
70 {
71  enum class negative_choice : int {};
72 
73  EXPECT_TRUE((std::is_same<unsigned, decltype(lmdb::to_native(choice(0)))>()));
75  (std::is_same<unsigned long, decltype(lmdb::to_native(big_choice(0)))>())
76  );
78  (std::is_same<int, decltype(lmdb::to_native(negative_choice(0)))>())
79  );
80 
81  EXPECT_EQ(unsigned(0), lmdb::to_native(choice(0)));
82  EXPECT_EQ(unsigned(0xffffffff), lmdb::to_native(choice(0xffffffff)));
83  EXPECT_EQ(-1, lmdb::to_native(negative_choice(-1)));
84 
85  // test constexpr
86  static_assert(100 == lmdb::to_native(choice(100)), "to_native failed");
87  static_assert(-100 == lmdb::to_native(negative_choice(-100)), "to_native failed");
88 }
89 
90 TEST(LMDB, Conversions)
91 {
92  struct one
93  {
94  big_choice i;
95  choice j;
96  };
97 
98  const one test{big_choice(100), choice(95)};
99  one test2{big_choice(1000), choice(950)};
100 
101  EXPECT_EQ(&test, lmdb::to_val(test).mv_data);
102  EXPECT_NE(&test2, lmdb::to_val(test).mv_data);
103  EXPECT_EQ(
104  &test,
105  static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test)).begin())
106  );
107  EXPECT_EQ(sizeof(test), lmdb::to_val(test).mv_size);
108  EXPECT_EQ(sizeof(test), lmdb::to_byte_span(lmdb::to_val(test)).size());
109 
110  EXPECT_EQ(&test2, lmdb::to_val(test2).mv_data);
111  EXPECT_NE(&test, lmdb::to_val(test2).mv_data);
112  EXPECT_EQ(
113  &test2,
114  static_cast<const void*>(lmdb::to_byte_span(lmdb::to_val(test2)).begin())
115  );
116  EXPECT_EQ(sizeof(test2), lmdb::to_val(test2).mv_size);
118 }
119 
120 TEST(LMDB, LessSort)
121 {
122  struct one
123  {
124  unsigned i;
125  unsigned j;
126  };
127 
128  struct two
129  {
130  unsigned i;
131  choice j;
132  };
133 
134  EXPECT_EQ(0, run_compare(0u, 0u, &lmdb::less<unsigned>));
135  EXPECT_EQ(-1, run_compare(0u, 1u, &lmdb::less<unsigned>));
136  EXPECT_EQ(1, run_compare(1u, 0u, &lmdb::less<unsigned>));
137 
138  EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>));
139  EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, &lmdb::less<unsigned, sizeof(unsigned)>));
140  EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, &lmdb::less<unsigned, sizeof(unsigned)>));
141 
142  EXPECT_EQ(0, run_compare<one>({0, 1}, {0, 1}, ELECTRONEUM_SORT_BY(one, j)));
143  EXPECT_EQ(-1, run_compare<one>({0, 0}, {0, 1}, ELECTRONEUM_SORT_BY(one, j)));
144  EXPECT_EQ(1, run_compare<one>({0, 1}, {0, 0}, ELECTRONEUM_SORT_BY(one, j)));
145 
146  EXPECT_EQ(0, run_compare<two>({0, choice(1)}, {0, choice(1)}, ELECTRONEUM_SORT_BY(two, j)));
147  EXPECT_EQ(-1, run_compare<two>({0, choice(0)}, {0, choice(1)}, ELECTRONEUM_SORT_BY(two, j)));
148  EXPECT_EQ(1, run_compare<two>({0, choice(1)}, {0, choice(0)}, ELECTRONEUM_SORT_BY(two, j)));
149 
150  // compare function addresses
151  EXPECT_EQ((ELECTRONEUM_SORT_BY(one, i)), (ELECTRONEUM_SORT_BY(two, i)));
152  EXPECT_EQ((ELECTRONEUM_SORT_BY(one, j)), (ELECTRONEUM_SORT_BY(two, j)));
153  EXPECT_NE((ELECTRONEUM_SORT_BY(one, i)), (ELECTRONEUM_SORT_BY(two, j)));
154  EXPECT_NE((ELECTRONEUM_SORT_BY(one, j)), (ELECTRONEUM_SORT_BY(two, i)));
155 }
156 
157 TEST(LMDB, SortCompare)
158 {
159  struct one
160  {
161  unsigned i;
162  bytes j;
163  };
164 
165  one test{55};
166  boost::iota(test.j.data, 10);
167 
168  const one test2 = test;
169 
170  EXPECT_EQ(0, run_compare(test, test2, ELECTRONEUM_COMPARE(one, j)));
171 
172  test.j.data[15] = 1;
173  EXPECT_GT(0, run_compare(test, test2, ELECTRONEUM_COMPARE(one, j)));
174 
175  test.j.data[15] = 100;
176  EXPECT_LT(0, run_compare(test, test2, ELECTRONEUM_COMPARE(one, j)));
177 }
178 
179 TEST(LMDB, Table)
180 {
181  struct one
182  {
183  bytes i;
184  bytes j;
185  };
186 
187  constexpr lmdb::basic_table<choice, bytes> test{"foo"};
188 
189  EXPECT_STREQ("foo", test.name);
190  static_assert(test.flags == 0, "bad flags");
191  static_assert(&lmdb::less<unsigned> == test.key_cmp, "bad key_cmp");
192  static_assert(test.value_cmp == nullptr, "bad value_cmp");
193  EXPECT_TRUE(test.get_value<bytes>(MDB_val{}).matches(std::errc::invalid_argument));
194 
196  "foo2", MDB_DUPSORT, &lmdb::compare<one>
197  };
198 
199  EXPECT_STREQ("foo2", test2.name);
201  EXPECT_EQ(&lmdb::less<unsigned long>, test2.key_cmp);
202  EXPECT_EQ(&lmdb::compare<one>, test2.value_cmp);
203  EXPECT_TRUE(test2.get_value<one>(MDB_val{}).matches(std::errc::invalid_argument));
204 
205  one record{};
206  boost::iota(record.i.data, 0);
207  boost::iota(record.i.data, 20);
208 
209  const one record_copy = ELECTRONEUM_UNWRAP(test2.get_value<one>(lmdb::to_val(record)));
210  EXPECT_TRUE(boost::equal(record.i.data, record_copy.i.data));
211  EXPECT_TRUE(boost::equal(record.j.data, record_copy.j.data));
212 
213  const bytes j_copy = ELECTRONEUM_UNWRAP(
214  test2.get_value<ELECTRONEUM_FIELD(one, j)>(lmdb::to_val(record))
215  );
216  EXPECT_TRUE(boost::equal(record.j.data, j_copy.data));
217 
218  EXPECT_TRUE(
219  test.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument)
220  );
221  EXPECT_TRUE(
222  test2.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument)
223  );
224 
225 
226  EXPECT_TRUE(
227  test.get_value_stream(choice(0), test_cursor{}).matches(std::errc::invalid_argument)
228  );
229  EXPECT_TRUE(
230  test2.get_value_stream(big_choice(0), test_cursor{}).matches(std::errc::invalid_argument)
231  );
232 }
233 
234 TEST(LMDB, InvalidDatabase)
235 {
237 
238  EXPECT_TRUE(test.resize().matches(std::errc::invalid_argument));
239  EXPECT_TRUE(test.create_read_txn().matches(std::errc::invalid_argument));
240  EXPECT_TRUE(test.reset_txn(lmdb::read_txn{}).matches(std::errc::invalid_argument));
241  EXPECT_TRUE(test.create_write_txn().matches(std::errc::invalid_argument));
242  EXPECT_TRUE(test.commit(lmdb::write_txn{}).matches(std::errc::invalid_argument));
243 
244  EXPECT_TRUE(
245  test.try_write( [](MDB_txn&) { return success(); } ).matches(std::errc::invalid_argument)
246  );
247 }
248 
249 TEST(LMDB, InvalidValueStream)
250 {
251  struct one
252  {
253  choice i;
254  choice j;
255  bytes k;
256  };
257 
258  lmdb::value_stream<one, close_test_cursor> test{test_cursor{}};
259 
260  EXPECT_TRUE((std::is_same<one, decltype(*(test.make_iterator()))>()));
261  EXPECT_TRUE((std::is_same<one, decltype(*(test.make_range().begin()))>()));
262  EXPECT_TRUE(
263  (std::is_same<bytes, decltype(*(test.make_iterator<ELECTRONEUM_FIELD(one, k)>()))>())
264  );
265  EXPECT_TRUE(
266  (std::is_same<bytes, decltype(*(test.make_range<ELECTRONEUM_FIELD(one, k)>().begin()))>())
267  );
268 
269  EXPECT_NO_THROW(test.reset());
270  EXPECT_EQ(0u, test.count());
271  EXPECT_TRUE(test.make_iterator().is_end());
272  EXPECT_TRUE(test.make_range().empty());
273  EXPECT_EQ(nullptr, test.give_cursor());
274 
275  EXPECT_EQ(0u, test.count());
276  EXPECT_TRUE(test.make_iterator().is_end());
277  EXPECT_TRUE(test.make_range().empty());
278  EXPECT_EQ(nullptr, test.give_cursor());
279 }
280 
281 TEST(LMDB, InvalidValueIterator)
282 {
283  struct one
284  {
285  choice i;
286  choice j;
287  bytes k;
288  };
289 
291 
292  EXPECT_TRUE((std::is_same<one, decltype(*test1)>()));
293  EXPECT_TRUE(
294  (std::is_same<bytes, decltype(test1.get_value<ELECTRONEUM_FIELD(one, k)>())>())
295  );
296 
297  EXPECT_TRUE(test1.is_end());
300  EXPECT_TRUE(test1.is_end());
301 
303 
304  EXPECT_TRUE(test2.is_end());
307  EXPECT_TRUE(test2.is_end());
308 
309  EXPECT_TRUE(test1.equal(test2));
310  EXPECT_TRUE(test2.equal(test1));
311  EXPECT_TRUE(test1 == test2);
312  EXPECT_TRUE(test2 == test1);
315 
317 
318  EXPECT_TRUE((std::is_same<bytes, decltype(*test3)>()));
319  EXPECT_TRUE((std::is_same<one, decltype(test3.get_value<one>())>()));
320  EXPECT_TRUE(
321  (std::is_same<choice, decltype(test1.get_value<ELECTRONEUM_FIELD(one, j)>())>())
322  );
323 
324  EXPECT_TRUE(test3.is_end());
327  EXPECT_TRUE(test3.is_end());
328 }
329 
330 TEST(LMDB, InvalidKeyStream)
331 {
332  struct one
333  {
334  choice i;
335  choice j;
336  bytes k;
337  };
338 
339  using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>;
340 
342 
343  EXPECT_TRUE((std::is_same<record, decltype(*(test.make_iterator()))>()));
344  EXPECT_TRUE((std::is_same<record, decltype(*(test.make_range().begin()))>()));
345 
346  EXPECT_NO_THROW(test.reset());
347  EXPECT_TRUE(test.make_iterator().is_end());
348  EXPECT_TRUE(test.make_range().empty());
349  EXPECT_EQ(nullptr, test.give_cursor());
350 
351  EXPECT_TRUE(test.make_iterator().is_end());
352  EXPECT_TRUE(test.make_range().empty());
353  EXPECT_EQ(nullptr, test.give_cursor());
354 }
355 
356 TEST(LMDB, InvalidKeyIterator)
357 {
358  struct one
359  {
360  choice i;
361  choice j;
362  bytes k;
363  };
364 
365  using record = std::pair<choice, boost::iterator_range<lmdb::value_iterator<one>>>;
366 
368 
369  EXPECT_TRUE((std::is_same<record, decltype(*test1)>()));
370  EXPECT_TRUE((std::is_same<choice, decltype(test1.get_key())>()));
371  EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_iterator()))>()));
372  EXPECT_TRUE((std::is_same<one, decltype(*(test1.make_value_range().begin()))>()));
373  EXPECT_TRUE(
374  (std::is_same<bytes, decltype(*(test1.make_value_iterator<ELECTRONEUM_FIELD(one, k)>()))>())
375  );
376  EXPECT_TRUE(
377  (std::is_same<bytes, decltype(*(test1.make_value_range<ELECTRONEUM_FIELD(one, k)>().begin()))>())
378  );
379 
380  EXPECT_TRUE(test1.is_end());
383  EXPECT_TRUE(test1.is_end());
384  EXPECT_TRUE(test1.make_value_iterator().is_end());
385  EXPECT_TRUE(test1.make_value_range().empty());
386 
388 
389  EXPECT_TRUE(test2.is_end());
392  EXPECT_TRUE(test2.is_end());
393  EXPECT_TRUE(test2.make_value_iterator().is_end());
394  EXPECT_TRUE(test2.make_value_range().empty());
395 
396  EXPECT_TRUE(test1.equal(test2));
397  EXPECT_TRUE(test2.equal(test1));
398  EXPECT_TRUE(test1 == test2);
399  EXPECT_TRUE(test2 == test1);
402 }
403 
404 
const uint32_t T[512]
#define EXPECT_NO_THROW(statement)
Definition: gtest.h:1845
#define EXPECT_GT(val1, val2)
Definition: gtest.h:1934
void test3()
#define EXPECT_TRUE(condition)
Definition: gtest.h:1859
#define EXPECT_STREQ(s1, s2)
Definition: gtest.h:1995
choice
Definition: lmdb.cpp:40
#define ELECTRONEUM_CURSOR(name)
Uses C++ type system to differentiate between cursors.
Definition: transaction.h:35
int() MDB_cmp_func(const MDB_val *a, const MDB_val *b)
A callback function used to compare two keys in a database.
Definition: lmdb.h:292
std::unique_ptr< MDB_txn, abort_write_txn > write_txn
Definition: transaction.h:94
#define ELECTRONEUM_SORT_BY(obj, field)
Expands to lmdb::less for the value field within obj.
Definition: util.h:44
std::unique_ptr< MDB_env, close_env > environment
Definition: database.h:51
std::unique_ptr< MDB_txn, release_read_txn > read_txn
Definition: transaction.h:93
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
Definition: lmdb.h:267
#define EXPECT_LT(val1, val2)
Definition: gtest.h:1930
big_choice
Definition: lmdb.cpp:41
Helper for grouping typical LMDB DBI options when key and value are fixed types.
Definition: table.h:27
#define MDB_DUPFIXED
Definition: lmdb.h:351
#define EXPECT_NE(val1, val2)
Definition: gtest.h:1926
#define ELECTRONEUM_COMPARE(obj, field)
Expands to lmdb::compare for the value field within obj.
Definition: util.h:51
#define ELECTRONEUM_FIELD(obj, field)
Definition: util.h:40
Generic structure used for passing keys and data in and out of the database.
Definition: lmdb.h:286
#define ELECTRONEUM_UNWRAP(...)
Definition: expect.h:60
void test2()
TEST(LMDB, Traits)
Definition: lmdb.cpp:58
MDB_val to_val(T &&value) noexcept
Definition: util.h:88
constexpr U to_native(T value) noexcept
Definition: util.h:81
#define EXPECT_FALSE(condition)
Definition: gtest.h:1862
typename std::conditional< std::is_enum< T >::value, std::underlying_type< T >, identity< T > >::type::type native_type
Definition: util.h:77
#define MDB_DUPSORT
Definition: lmdb.h:345
Manages a LMDB environment for safe memory-map resizing. Thread-safe.
Definition: database.h:64
constexpr epee::span< const std::uint8_t > to_byte_span(MDB_val value) noexcept
Definition: util.h:97
#define EXPECT_EQ(val1, val2)
Definition: gtest.h:1922
void test1()