Electroneum
Loading...
Searching...
No Matches
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
38namespace
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
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
69TEST(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
90TEST(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
120TEST(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
155}
156
157TEST(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
179TEST(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
197 };
198
199 EXPECT_STREQ("foo2", test2.name);
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
219 test.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument)
220 );
222 test2.get_key_stream(test_cursor{}).matches(std::errc::invalid_argument)
223 );
224
225
227 test.get_value_stream(choice(0), test_cursor{}).matches(std::errc::invalid_argument)
228 );
230 test2.get_value_stream(big_choice(0), test_cursor{}).matches(std::errc::invalid_argument)
231 );
232}
233
234TEST(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
245 test.try_write( [](MDB_txn&) { return success(); } ).matches(std::errc::invalid_argument)
246 );
247}
248
249TEST(LMDB, InvalidValueStream)
250{
251 struct one
252 {
253 choice i;
254 choice j;
255 bytes k;
256 };
257
259
260 EXPECT_TRUE((std::is_same<one, decltype(*(test.make_iterator()))>()));
261 EXPECT_TRUE((std::is_same<one, decltype(*(test.make_range().begin()))>()));
263 (std::is_same<bytes, decltype(*(test.make_iterator<ELECTRONEUM_FIELD(one, k)>()))>())
264 );
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
281TEST(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)>()));
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));
315
317
318 EXPECT_TRUE((std::is_same<bytes, decltype(*test3)>()));
319 EXPECT_TRUE((std::is_same<one, decltype(test3.get_value<one>())>()));
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
330TEST(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
356TEST(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()))>()));
374 (std::is_same<bytes, decltype(*(test1.make_value_iterator<ELECTRONEUM_FIELD(one, k)>()))>())
375 );
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));
402}
403
404
void test2()
void test1()
void test3()
Manages a LMDB environment for safe memory-map resizing. Thread-safe.
Definition database.h:65
#define ELECTRONEUM_UNWRAP(...)
Definition expect.h:60
#define EXPECT_NO_THROW(statement)
Definition gtest.h:1845
#define EXPECT_EQ(val1, val2)
Definition gtest.h:1922
#define EXPECT_NE(val1, val2)
Definition gtest.h:1926
#define EXPECT_GT(val1, val2)
Definition gtest.h:1934
#define EXPECT_TRUE(condition)
Definition gtest.h:1859
#define EXPECT_STREQ(s1, s2)
Definition gtest.h:1995
#define TEST(test_case_name, test_name)
Definition gtest.h:2187
#define EXPECT_FALSE(condition)
Definition gtest.h:1862
#define EXPECT_LT(val1, val2)
Definition gtest.h:1930
#define MDB_DUPFIXED
Definition lmdb.h:351
#define MDB_DUPSORT
Definition lmdb.h:345
struct MDB_txn MDB_txn
Opaque structure for a transaction handle.
Definition lmdb.h:267
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
std::unique_ptr< MDB_env, close_env > environment
Definition database.h:51
constexpr epee::span< const std::uint8_t > to_byte_span(MDB_val value) noexcept
Definition util.h:97
int compare(MDB_val const *left, MDB_val const *right) noexcept
Definition util.h:135
int less(MDB_val const *left, MDB_val const *right) noexcept
Definition util.h:111
constexpr U to_native(T value) noexcept
Definition util.h:81
MDB_val to_val(T &&value) noexcept
Definition util.h:88
std::unique_ptr< MDB_txn, release_read_txn > read_txn
Definition transaction.h:93
typename std::conditional< std::is_enum< T >::value, std::underlying_type< T >, identity< T > >::type::type native_type
Definition util.h:75
#define ELECTRONEUM_FIELD(obj, field)
Definition util.h:40
#define ELECTRONEUM_COMPARE(obj, field)
Expands to lmdb::compare for the value field within obj.
Definition util.h:51
#define ELECTRONEUM_SORT_BY(obj, field)
Expands to lmdb::less for the value field within obj.
Definition util.h:44
Generic structure used for passing keys and data in and out of the database.
Definition lmdb.h:286
Helper for grouping typical LMDB DBI options when key and value are fixed types.
Definition table.h:28
#define ELECTRONEUM_CURSOR(name)
Uses C++ type system to differentiate between cursors.
Definition transaction.h:35
#define T(x)