1 /* This is free and unencumbered software released into the public domain. */
2
3 #ifndef LMDBXX_H
4 #define LMDBXX_H
5
6 /**
7 * <lmdb++.h> - C++11 wrapper for LMDB.
8 *
9 * @author Arto Bendiken <arto@bendiken.net>
10 * @see https://sourceforge.net/projects/lmdbxx/
11 */
12
13 #ifndef __cplusplus
14 #error "<lmdb++.h> requires a C++ compiler"
15 #endif
16
17 #if __cplusplus < 201103L
18 #if !defined(_MSC_VER) || _MSC_VER < 1900
19 #error "<lmdb++.h> requires a C++11 compiler (CXXFLAGS='-std=c++11')"
20 #endif // _MSC_VER check
21 #endif
22
23 ////////////////////////////////////////////////////////////////////////////////
24
25 #include <lmdb.h> /* for MDB_*, mdb_*() */
26
27 #ifdef LMDBXX_DEBUG
28 #include <cassert> /* for assert() */
29 #endif
30 #include <cstddef> /* for std::size_t */
31 #include <cstdio> /* for std::snprintf() */
32 #include <cstring> /* for std::strlen() */
33 #include <stdexcept> /* for std::runtime_error */
34 #include <string> /* for std::string */
35 #include <type_traits> /* for std::is_pod<> */
36
37 namespace lmdb {
38 using mode = mdb_mode_t;
39 }
40
41 ////////////////////////////////////////////////////////////////////////////////
42 /* Error Handling */
43
44 namespace lmdb {
45 class error;
46 class logic_error;
47 class fatal_error;
48 class runtime_error;
49 class key_exist_error;
50 class not_found_error;
51 class corrupted_error;
52 class panic_error;
53 class version_mismatch_error;
54 class map_full_error;
55 class bad_dbi_error;
56 }
57
58 /**
59 * Base class for LMDB exception conditions.
60 *
61 * @see http://symas.com/mdb/doc/group__errors.html
62 */
63 class lmdb::error : public std::runtime_error {
64 protected:
65 const int _code;
66
67 public:
68 /**
69 * Throws an error based on the given LMDB return code.
70 */
71 [[noreturn]] static inline void raise(const char* origin, int rc);
72
73 /**
74 * Constructor.
75 */
error(const char * const origin,const int rc)76 error(const char* const origin,
77 const int rc) noexcept
78 : runtime_error{origin},
79 _code{rc} {}
80
81 /**
82 * Returns the underlying LMDB error code.
83 */
code()84 int code() const noexcept {
85 return _code;
86 }
87
88 /**
89 * Returns the origin of the LMDB error.
90 */
origin()91 const char* origin() const noexcept {
92 return runtime_error::what();
93 }
94
95 /**
96 * Returns the underlying LMDB error code.
97 */
what()98 virtual const char* what() const noexcept {
99 #if !defined(__APPLE__)
100 static thread_local char buffer[1024];
101 #else
102 static char buffer[1024]; /* FIXME: need to provide locking */
103 #endif
104 #if defined(_MSC_VER)
105 snprintf(buffer, sizeof(buffer),
106 "%s: %s", origin(), ::mdb_strerror(code()));
107 #else
108 std::snprintf(buffer, sizeof(buffer),
109 "%s: %s", origin(), ::mdb_strerror(code()));
110 #endif
111 return buffer;
112 }
113 };
114
115 /**
116 * Base class for logic error conditions.
117 */
118 class lmdb::logic_error : public lmdb::error {
119 public:
120 using error::error;
121 };
122
123 /**
124 * Base class for fatal error conditions.
125 */
126 class lmdb::fatal_error : public lmdb::error {
127 public:
128 using error::error;
129 };
130
131 /**
132 * Base class for runtime error conditions.
133 */
134 class lmdb::runtime_error : public lmdb::error {
135 public:
136 using error::error;
137 };
138
139 /**
140 * Exception class for `MDB_KEYEXIST` errors.
141 *
142 * @see http://symas.com/mdb/doc/group__errors.html#ga05dc5bbcc7da81a7345bd8676e8e0e3b
143 */
144 class lmdb::key_exist_error final : public lmdb::runtime_error {
145 public:
146 using runtime_error::runtime_error;
147 };
148
149 /**
150 * Exception class for `MDB_NOTFOUND` errors.
151 *
152 * @see http://symas.com/mdb/doc/group__errors.html#gabeb52e4c4be21b329e31c4add1b71926
153 */
154 class lmdb::not_found_error final : public lmdb::runtime_error {
155 public:
156 using runtime_error::runtime_error;
157 };
158
159 /**
160 * Exception class for `MDB_CORRUPTED` errors.
161 *
162 * @see http://symas.com/mdb/doc/group__errors.html#gaf8148bf1b85f58e264e57194bafb03ef
163 */
164 class lmdb::corrupted_error final : public lmdb::fatal_error {
165 public:
166 using fatal_error::fatal_error;
167 };
168
169 /**
170 * Exception class for `MDB_PANIC` errors.
171 *
172 * @see http://symas.com/mdb/doc/group__errors.html#gae37b9aedcb3767faba3de8c1cf6d3473
173 */
174 class lmdb::panic_error final : public lmdb::fatal_error {
175 public:
176 using fatal_error::fatal_error;
177 };
178
179 /**
180 * Exception class for `MDB_VERSION_MISMATCH` errors.
181 *
182 * @see http://symas.com/mdb/doc/group__errors.html#ga909b2db047fa90fb0d37a78f86a6f99b
183 */
184 class lmdb::version_mismatch_error final : public lmdb::fatal_error {
185 public:
186 using fatal_error::fatal_error;
187 };
188
189 /**
190 * Exception class for `MDB_MAP_FULL` errors.
191 *
192 * @see http://symas.com/mdb/doc/group__errors.html#ga0a83370402a060c9175100d4bbfb9f25
193 */
194 class lmdb::map_full_error final : public lmdb::runtime_error {
195 public:
196 using runtime_error::runtime_error;
197 };
198
199 /**
200 * Exception class for `MDB_BAD_DBI` errors.
201 *
202 * @since 0.9.14 (2014/09/20)
203 * @see http://symas.com/mdb/doc/group__errors.html#gab4c82e050391b60a18a5df08d22a7083
204 */
205 class lmdb::bad_dbi_error final : public lmdb::runtime_error {
206 public:
207 using runtime_error::runtime_error;
208 };
209
210 inline void
raise(const char * const origin,const int rc)211 lmdb::error::raise(const char* const origin,
212 const int rc) {
213 switch (rc) {
214 case MDB_KEYEXIST: throw key_exist_error{origin, rc};
215 case MDB_NOTFOUND: throw not_found_error{origin, rc};
216 case MDB_CORRUPTED: throw corrupted_error{origin, rc};
217 case MDB_PANIC: throw panic_error{origin, rc};
218 case MDB_VERSION_MISMATCH: throw version_mismatch_error{origin, rc};
219 case MDB_MAP_FULL: throw map_full_error{origin, rc};
220 #ifdef MDB_BAD_DBI
221 case MDB_BAD_DBI: throw bad_dbi_error{origin, rc};
222 #endif
223 default: throw lmdb::runtime_error{origin, rc};
224 }
225 }
226
227 ////////////////////////////////////////////////////////////////////////////////
228 /* Procedural Interface: Metadata */
229
230 namespace lmdb {
231 // TODO: mdb_version()
232 // TODO: mdb_strerror()
233 }
234
235 ////////////////////////////////////////////////////////////////////////////////
236 /* Procedural Interface: Environment */
237
238 namespace lmdb {
239 static inline void env_create(MDB_env** env);
240 static inline void env_open(MDB_env* env,
241 const char* path, unsigned int flags, mode mode);
242 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
243 static inline void env_copy(MDB_env* env, const char* path, unsigned int flags);
244 static inline void env_copy_fd(MDB_env* env, mdb_filehandle_t fd, unsigned int flags);
245 #else
246 static inline void env_copy(MDB_env* env, const char* path);
247 static inline void env_copy_fd(MDB_env* env, mdb_filehandle_t fd);
248 #endif
249 static inline void env_stat(MDB_env* env, MDB_stat* stat);
250 static inline void env_info(MDB_env* env, MDB_envinfo* stat);
251 static inline void env_sync(MDB_env* env, bool force);
252 static inline void env_close(MDB_env* env) noexcept;
253 static inline void env_set_flags(MDB_env* env, unsigned int flags, bool onoff);
254 static inline void env_get_flags(MDB_env* env, unsigned int* flags);
255 static inline void env_get_path(MDB_env* env, const char** path);
256 static inline void env_get_fd(MDB_env* env, mdb_filehandle_t* fd);
257 static inline void env_set_mapsize(MDB_env* env, std::size_t size);
258 static inline void env_set_max_readers(MDB_env* env, unsigned int count);
259 static inline void env_get_max_readers(MDB_env* env, unsigned int* count);
260 static inline void env_set_max_dbs(MDB_env* env, MDB_dbi count);
261 static inline unsigned int env_get_max_keysize(MDB_env* env);
262 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
263 static inline void env_set_userctx(MDB_env* env, void* ctx);
264 static inline void* env_get_userctx(MDB_env* env);
265 #endif
266 // TODO: mdb_env_set_assert()
267 // TODO: mdb_reader_list()
268 // TODO: mdb_reader_check()
269 }
270
271 /**
272 * @throws lmdb::error on failure
273 * @see http://symas.com/mdb/doc/group__mdb.html#gaad6be3d8dcd4ea01f8df436f41d158d4
274 */
275 static inline void
env_create(MDB_env ** env)276 lmdb::env_create(MDB_env** env) {
277 const int rc = ::mdb_env_create(env);
278 if (rc != MDB_SUCCESS) {
279 error::raise("mdb_env_create", rc);
280 }
281 }
282
283 /**
284 * @throws lmdb::error on failure
285 * @see http://symas.com/mdb/doc/group__mdb.html#ga32a193c6bf4d7d5c5d579e71f22e9340
286 */
287 static inline void
env_open(MDB_env * const env,const char * const path,const unsigned int flags,const mode mode)288 lmdb::env_open(MDB_env* const env,
289 const char* const path,
290 const unsigned int flags,
291 const mode mode) {
292 const int rc = ::mdb_env_open(env, path, flags, mode);
293 if (rc != MDB_SUCCESS) {
294 error::raise("mdb_env_open", rc);
295 }
296 }
297
298 /**
299 * @throws lmdb::error on failure
300 * @see http://symas.com/mdb/doc/group__mdb.html#ga3bf50d7793b36aaddf6b481a44e24244
301 * @see http://symas.com/mdb/doc/group__mdb.html#ga5d51d6130325f7353db0955dbedbc378
302 */
303 static inline void
304 lmdb::env_copy(MDB_env* const env,
305 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
306 const char* const path,
307 const unsigned int flags = 0) {
308 const int rc = ::mdb_env_copy2(env, path, flags);
309 #else
310 const char* const path) {
311 const int rc = ::mdb_env_copy(env, path);
312 #endif
313 if (rc != MDB_SUCCESS) {
314 error::raise("mdb_env_copy2", rc);
315 }
316 }
317
318 /**
319 * @throws lmdb::error on failure
320 * @see http://symas.com/mdb/doc/group__mdb.html#ga5040d0de1f14000fa01fc0b522ff1f86
321 * @see http://symas.com/mdb/doc/group__mdb.html#ga470b0bcc64ac417de5de5930f20b1a28
322 */
323 static inline void
324 lmdb::env_copy_fd(MDB_env* const env,
325 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 14)
326 const mdb_filehandle_t fd,
327 const unsigned int flags = 0) {
328 const int rc = ::mdb_env_copyfd2(env, fd, flags);
329 #else
330 const mdb_filehandle_t fd) {
331 const int rc = ::mdb_env_copyfd(env, fd);
332 #endif
333 if (rc != MDB_SUCCESS) {
334 error::raise("mdb_env_copyfd2", rc);
335 }
336 }
337
338 /**
339 * @throws lmdb::error on failure
340 * @see http://symas.com/mdb/doc/group__mdb.html#gaf881dca452050efbd434cd16e4bae255
341 */
342 static inline void
env_stat(MDB_env * const env,MDB_stat * const stat)343 lmdb::env_stat(MDB_env* const env,
344 MDB_stat* const stat) {
345 const int rc = ::mdb_env_stat(env, stat);
346 if (rc != MDB_SUCCESS) {
347 error::raise("mdb_env_stat", rc);
348 }
349 }
350
351 /**
352 * @throws lmdb::error on failure
353 * @see http://symas.com/mdb/doc/group__mdb.html#ga18769362c7e7d6cf91889a028a5c5947
354 */
355 static inline void
env_info(MDB_env * const env,MDB_envinfo * const stat)356 lmdb::env_info(MDB_env* const env,
357 MDB_envinfo* const stat) {
358 const int rc = ::mdb_env_info(env, stat);
359 if (rc != MDB_SUCCESS) {
360 error::raise("mdb_env_info", rc);
361 }
362 }
363
364 /**
365 * @throws lmdb::error on failure
366 * @see http://symas.com/mdb/doc/group__mdb.html#ga85e61f05aa68b520cc6c3b981dba5037
367 */
368 static inline void
369 lmdb::env_sync(MDB_env* const env,
370 const bool force = true) {
371 const int rc = ::mdb_env_sync(env, force);
372 if (rc != MDB_SUCCESS) {
373 error::raise("mdb_env_sync", rc);
374 }
375 }
376
377 /**
378 * @see http://symas.com/mdb/doc/group__mdb.html#ga4366c43ada8874588b6a62fbda2d1e95
379 */
380 static inline void
env_close(MDB_env * const env)381 lmdb::env_close(MDB_env* const env) noexcept {
382 ::mdb_env_close(env);
383 }
384
385 /**
386 * @throws lmdb::error on failure
387 * @see http://symas.com/mdb/doc/group__mdb.html#ga83f66cf02bfd42119451e9468dc58445
388 */
389 static inline void
390 lmdb::env_set_flags(MDB_env* const env,
391 const unsigned int flags,
392 const bool onoff = true) {
393 const int rc = ::mdb_env_set_flags(env, flags, onoff ? 1 : 0);
394 if (rc != MDB_SUCCESS) {
395 error::raise("mdb_env_set_flags", rc);
396 }
397 }
398
399 /**
400 * @throws lmdb::error on failure
401 * @see http://symas.com/mdb/doc/group__mdb.html#ga2733aefc6f50beb49dd0c6eb19b067d9
402 */
403 static inline void
env_get_flags(MDB_env * const env,unsigned int * const flags)404 lmdb::env_get_flags(MDB_env* const env,
405 unsigned int* const flags) {
406 const int rc = ::mdb_env_get_flags(env, flags);
407 if (rc != MDB_SUCCESS) {
408 error::raise("mdb_env_get_flags", rc);
409 }
410 }
411
412 /**
413 * @throws lmdb::error on failure
414 * @see http://symas.com/mdb/doc/group__mdb.html#gac699fdd8c4f8013577cb933fb6a757fe
415 */
416 static inline void
env_get_path(MDB_env * const env,const char ** path)417 lmdb::env_get_path(MDB_env* const env,
418 const char** path) {
419 const int rc = ::mdb_env_get_path(env, path);
420 if (rc != MDB_SUCCESS) {
421 error::raise("mdb_env_get_path", rc);
422 }
423 }
424
425 /**
426 * @throws lmdb::error on failure
427 * @see http://symas.com/mdb/doc/group__mdb.html#gaf1570e7c0e5a5d860fef1032cec7d5f2
428 */
429 static inline void
env_get_fd(MDB_env * const env,mdb_filehandle_t * const fd)430 lmdb::env_get_fd(MDB_env* const env,
431 mdb_filehandle_t* const fd) {
432 const int rc = ::mdb_env_get_fd(env, fd);
433 if (rc != MDB_SUCCESS) {
434 error::raise("mdb_env_get_fd", rc);
435 }
436 }
437
438 /**
439 * @throws lmdb::error on failure
440 * @see http://symas.com/mdb/doc/group__mdb.html#gaa2506ec8dab3d969b0e609cd82e619e5
441 */
442 static inline void
env_set_mapsize(MDB_env * const env,const std::size_t size)443 lmdb::env_set_mapsize(MDB_env* const env,
444 const std::size_t size) {
445 const int rc = ::mdb_env_set_mapsize(env, size);
446 if (rc != MDB_SUCCESS) {
447 error::raise("mdb_env_set_mapsize", rc);
448 }
449 }
450
451 /**
452 * @throws lmdb::error on failure
453 * @see http://symas.com/mdb/doc/group__mdb.html#gae687966c24b790630be2a41573fe40e2
454 */
455 static inline void
env_set_max_readers(MDB_env * const env,const unsigned int count)456 lmdb::env_set_max_readers(MDB_env* const env,
457 const unsigned int count) {
458 const int rc = ::mdb_env_set_maxreaders(env, count);
459 if (rc != MDB_SUCCESS) {
460 error::raise("mdb_env_set_maxreaders", rc);
461 }
462 }
463
464 /**
465 * @throws lmdb::error on failure
466 * @see http://symas.com/mdb/doc/group__mdb.html#ga70e143cf11760d869f754c9c9956e6cc
467 */
468 static inline void
env_get_max_readers(MDB_env * const env,unsigned int * const count)469 lmdb::env_get_max_readers(MDB_env* const env,
470 unsigned int* const count) {
471 const int rc = ::mdb_env_get_maxreaders(env, count);
472 if (rc != MDB_SUCCESS) {
473 error::raise("mdb_env_get_maxreaders", rc);
474 }
475 }
476
477 /**
478 * @throws lmdb::error on failure
479 * @see http://symas.com/mdb/doc/group__mdb.html#gaa2fc2f1f37cb1115e733b62cab2fcdbc
480 */
481 static inline void
env_set_max_dbs(MDB_env * const env,const MDB_dbi count)482 lmdb::env_set_max_dbs(MDB_env* const env,
483 const MDB_dbi count) {
484 const int rc = ::mdb_env_set_maxdbs(env, count);
485 if (rc != MDB_SUCCESS) {
486 error::raise("mdb_env_set_maxdbs", rc);
487 }
488 }
489
490 /**
491 * @see http://symas.com/mdb/doc/group__mdb.html#gaaf0be004f33828bf2fb09d77eb3cef94
492 */
493 static inline unsigned int
env_get_max_keysize(MDB_env * const env)494 lmdb::env_get_max_keysize(MDB_env* const env) {
495 const int rc = ::mdb_env_get_maxkeysize(env);
496 #ifdef LMDBXX_DEBUG
497 assert(rc >= 0);
498 #endif
499 return static_cast<unsigned int>(rc);
500 }
501
502 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
503 /**
504 * @throws lmdb::error on failure
505 * @since 0.9.11 (2014/01/15)
506 * @see http://symas.com/mdb/doc/group__mdb.html#gaf2fe09eb9c96eeb915a76bf713eecc46
507 */
508 static inline void
env_set_userctx(MDB_env * const env,void * const ctx)509 lmdb::env_set_userctx(MDB_env* const env,
510 void* const ctx) {
511 const int rc = ::mdb_env_set_userctx(env, ctx);
512 if (rc != MDB_SUCCESS) {
513 error::raise("mdb_env_set_userctx", rc);
514 }
515 }
516 #endif
517
518 #if MDB_VERSION_FULL >= MDB_VERINT(0, 9, 11)
519 /**
520 * @since 0.9.11 (2014/01/15)
521 * @see http://symas.com/mdb/doc/group__mdb.html#ga45df6a4fb150cda2316b5ae224ba52f1
522 */
523 static inline void*
env_get_userctx(MDB_env * const env)524 lmdb::env_get_userctx(MDB_env* const env) {
525 return ::mdb_env_get_userctx(env);
526 }
527 #endif
528
529 ////////////////////////////////////////////////////////////////////////////////
530 /* Procedural Interface: Transactions */
531
532 namespace lmdb {
533 static inline void txn_begin(
534 MDB_env* env, MDB_txn* parent, unsigned int flags, MDB_txn** txn);
535 static inline MDB_env* txn_env(MDB_txn* txn) noexcept;
536 #ifdef LMDBXX_TXN_ID
537 static inline std::size_t txn_id(MDB_txn* txn) noexcept;
538 #endif
539 static inline void txn_commit(MDB_txn* txn);
540 static inline void txn_abort(MDB_txn* txn) noexcept;
541 static inline void txn_reset(MDB_txn* txn) noexcept;
542 static inline void txn_renew(MDB_txn* txn);
543 }
544
545 /**
546 * @throws lmdb::error on failure
547 * @see http://symas.com/mdb/doc/group__mdb.html#gad7ea55da06b77513609efebd44b26920
548 */
549 static inline void
txn_begin(MDB_env * const env,MDB_txn * const parent,const unsigned int flags,MDB_txn ** txn)550 lmdb::txn_begin(MDB_env* const env,
551 MDB_txn* const parent,
552 const unsigned int flags,
553 MDB_txn** txn) {
554 const int rc = ::mdb_txn_begin(env, parent, flags, txn);
555 if (rc != MDB_SUCCESS) {
556 error::raise("mdb_txn_begin", rc);
557 }
558 }
559
560 /**
561 * @see http://symas.com/mdb/doc/group__mdb.html#gaeb17735b8aaa2938a78a45cab85c06a0
562 */
563 static inline MDB_env*
txn_env(MDB_txn * const txn)564 lmdb::txn_env(MDB_txn* const txn) noexcept {
565 return ::mdb_txn_env(txn);
566 }
567
568 #ifdef LMDBXX_TXN_ID
569 /**
570 * @note Only available in HEAD, not yet in any 0.9.x release (as of 0.9.16).
571 */
572 static inline std::size_t
txn_id(MDB_txn * const txn)573 lmdb::txn_id(MDB_txn* const txn) noexcept {
574 return ::mdb_txn_id(txn);
575 }
576 #endif
577
578 /**
579 * @throws lmdb::error on failure
580 * @see http://symas.com/mdb/doc/group__mdb.html#ga846fbd6f46105617ac9f4d76476f6597
581 */
582 static inline void
txn_commit(MDB_txn * const txn)583 lmdb::txn_commit(MDB_txn* const txn) {
584 const int rc = ::mdb_txn_commit(txn);
585 if (rc != MDB_SUCCESS) {
586 error::raise("mdb_txn_commit", rc);
587 }
588 }
589
590 /**
591 * @see http://symas.com/mdb/doc/group__mdb.html#ga73a5938ae4c3239ee11efa07eb22b882
592 */
593 static inline void
txn_abort(MDB_txn * const txn)594 lmdb::txn_abort(MDB_txn* const txn) noexcept {
595 ::mdb_txn_abort(txn);
596 }
597
598 /**
599 * @see http://symas.com/mdb/doc/group__mdb.html#ga02b06706f8a66249769503c4e88c56cd
600 */
601 static inline void
txn_reset(MDB_txn * const txn)602 lmdb::txn_reset(MDB_txn* const txn) noexcept {
603 ::mdb_txn_reset(txn);
604 }
605
606 /**
607 * @throws lmdb::error on failure
608 * @see http://symas.com/mdb/doc/group__mdb.html#ga6c6f917959517ede1c504cf7c720ce6d
609 */
610 static inline void
txn_renew(MDB_txn * const txn)611 lmdb::txn_renew(MDB_txn* const txn) {
612 const int rc = ::mdb_txn_renew(txn);
613 if (rc != MDB_SUCCESS) {
614 error::raise("mdb_txn_renew", rc);
615 }
616 }
617
618 ////////////////////////////////////////////////////////////////////////////////
619 /* Procedural Interface: Databases */
620
621 namespace lmdb {
622 static inline void dbi_open(
623 MDB_txn* txn, const char* name, unsigned int flags, MDB_dbi* dbi);
624 static inline void dbi_stat(MDB_txn* txn, MDB_dbi dbi, MDB_stat* stat);
625 static inline void dbi_flags(MDB_txn* txn, MDB_dbi dbi, unsigned int* flags);
626 static inline void dbi_close(MDB_env* env, MDB_dbi dbi) noexcept;
627 static inline void dbi_drop(MDB_txn* txn, MDB_dbi dbi, bool del);
628 static inline void dbi_set_compare(MDB_txn* txn, MDB_dbi dbi, MDB_cmp_func* cmp);
629 static inline void dbi_set_dupsort(MDB_txn* txn, MDB_dbi dbi, MDB_cmp_func* cmp);
630 static inline void dbi_set_relfunc(MDB_txn* txn, MDB_dbi dbi, MDB_rel_func* rel);
631 static inline void dbi_set_relctx(MDB_txn* txn, MDB_dbi dbi, void* ctx);
632 static inline bool dbi_get(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, MDB_val* data);
633 static inline bool dbi_put(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, MDB_val* data, unsigned int flags);
634 static inline bool dbi_del(MDB_txn* txn, MDB_dbi dbi, const MDB_val* key, const MDB_val* data);
635 // TODO: mdb_cmp()
636 // TODO: mdb_dcmp()
637 }
638
639 /**
640 * @throws lmdb::error on failure
641 * @see http://symas.com/mdb/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a
642 */
643 static inline void
dbi_open(MDB_txn * const txn,const char * const name,const unsigned int flags,MDB_dbi * const dbi)644 lmdb::dbi_open(MDB_txn* const txn,
645 const char* const name,
646 const unsigned int flags,
647 MDB_dbi* const dbi) {
648 const int rc = ::mdb_dbi_open(txn, name, flags, dbi);
649 if (rc != MDB_SUCCESS) {
650 error::raise("mdb_dbi_open", rc);
651 }
652 }
653
654 /**
655 * @throws lmdb::error on failure
656 * @see http://symas.com/mdb/doc/group__mdb.html#gae6c1069febe94299769dbdd032fadef6
657 */
658 static inline void
dbi_stat(MDB_txn * const txn,const MDB_dbi dbi,MDB_stat * const result)659 lmdb::dbi_stat(MDB_txn* const txn,
660 const MDB_dbi dbi,
661 MDB_stat* const result) {
662 const int rc = ::mdb_stat(txn, dbi, result);
663 if (rc != MDB_SUCCESS) {
664 error::raise("mdb_stat", rc);
665 }
666 }
667
668 /**
669 * @throws lmdb::error on failure
670 * @see http://symas.com/mdb/doc/group__mdb.html#ga95ba4cb721035478a8705e57b91ae4d4
671 */
672 static inline void
dbi_flags(MDB_txn * const txn,const MDB_dbi dbi,unsigned int * const flags)673 lmdb::dbi_flags(MDB_txn* const txn,
674 const MDB_dbi dbi,
675 unsigned int* const flags) {
676 const int rc = ::mdb_dbi_flags(txn, dbi, flags);
677 if (rc != MDB_SUCCESS) {
678 error::raise("mdb_dbi_flags", rc);
679 }
680 }
681
682 /**
683 * @see http://symas.com/mdb/doc/group__mdb.html#ga52dd98d0c542378370cd6b712ff961b5
684 */
685 static inline void
dbi_close(MDB_env * const env,const MDB_dbi dbi)686 lmdb::dbi_close(MDB_env* const env,
687 const MDB_dbi dbi) noexcept {
688 ::mdb_dbi_close(env, dbi);
689 }
690
691 /**
692 * @see http://symas.com/mdb/doc/group__mdb.html#gab966fab3840fc54a6571dfb32b00f2db
693 */
694 static inline void
695 lmdb::dbi_drop(MDB_txn* const txn,
696 const MDB_dbi dbi,
697 const bool del = false) {
698 const int rc = ::mdb_drop(txn, dbi, del ? 1 : 0);
699 if (rc != MDB_SUCCESS) {
700 error::raise("mdb_drop", rc);
701 }
702 }
703
704 /**
705 * @throws lmdb::error on failure
706 * @see http://symas.com/mdb/doc/group__mdb.html#ga68e47ffcf72eceec553c72b1784ee0fe
707 */
708 static inline void
709 lmdb::dbi_set_compare(MDB_txn* const txn,
710 const MDB_dbi dbi,
711 MDB_cmp_func* const cmp = nullptr) {
712 const int rc = ::mdb_set_compare(txn, dbi, cmp);
713 if (rc != MDB_SUCCESS) {
714 error::raise("mdb_set_compare", rc);
715 }
716 }
717
718 /**
719 * @throws lmdb::error on failure
720 * @see http://symas.com/mdb/doc/group__mdb.html#gacef4ec3dab0bbd9bc978b73c19c879ae
721 */
722 static inline void
723 lmdb::dbi_set_dupsort(MDB_txn* const txn,
724 const MDB_dbi dbi,
725 MDB_cmp_func* const cmp = nullptr) {
726 const int rc = ::mdb_set_dupsort(txn, dbi, cmp);
727 if (rc != MDB_SUCCESS) {
728 error::raise("mdb_set_dupsort", rc);
729 }
730 }
731
732 /**
733 * @throws lmdb::error on failure
734 * @see http://symas.com/mdb/doc/group__mdb.html#ga697d82c7afe79f142207ad5adcdebfeb
735 */
736 static inline void
dbi_set_relfunc(MDB_txn * const txn,const MDB_dbi dbi,MDB_rel_func * const rel)737 lmdb::dbi_set_relfunc(MDB_txn* const txn,
738 const MDB_dbi dbi,
739 MDB_rel_func* const rel) {
740 const int rc = ::mdb_set_relfunc(txn, dbi, rel);
741 if (rc != MDB_SUCCESS) {
742 error::raise("mdb_set_relfunc", rc);
743 }
744 }
745
746 /**
747 * @throws lmdb::error on failure
748 * @see http://symas.com/mdb/doc/group__mdb.html#ga7c34246308cee01724a1839a8f5cc594
749 */
750 static inline void
dbi_set_relctx(MDB_txn * const txn,const MDB_dbi dbi,void * const ctx)751 lmdb::dbi_set_relctx(MDB_txn* const txn,
752 const MDB_dbi dbi,
753 void* const ctx) {
754 const int rc = ::mdb_set_relctx(txn, dbi, ctx);
755 if (rc != MDB_SUCCESS) {
756 error::raise("mdb_set_relctx", rc);
757 }
758 }
759
760 /**
761 * @retval true if the key/value pair was retrieved
762 * @retval false if the key wasn't found
763 * @see http://symas.com/mdb/doc/group__mdb.html#ga8bf10cd91d3f3a83a34d04ce6b07992d
764 */
765 static inline bool
dbi_get(MDB_txn * const txn,const MDB_dbi dbi,const MDB_val * const key,MDB_val * const data)766 lmdb::dbi_get(MDB_txn* const txn,
767 const MDB_dbi dbi,
768 const MDB_val* const key,
769 MDB_val* const data) {
770 const int rc = ::mdb_get(txn, dbi, const_cast<MDB_val*>(key), data);
771 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
772 error::raise("mdb_get", rc);
773 }
774 return (rc == MDB_SUCCESS);
775 }
776
777 /**
778 * @retval true if the key/value pair was inserted
779 * @retval false if the key already existed
780 * @see http://symas.com/mdb/doc/group__mdb.html#ga4fa8573d9236d54687c61827ebf8cac0
781 */
782 static inline bool
783 lmdb::dbi_put(MDB_txn* const txn,
784 const MDB_dbi dbi,
785 const MDB_val* const key,
786 MDB_val* const data,
787 const unsigned int flags = 0) {
788 const int rc = ::mdb_put(txn, dbi, const_cast<MDB_val*>(key), data, flags);
789 if (rc != MDB_SUCCESS && rc != MDB_KEYEXIST) {
790 error::raise("mdb_put", rc);
791 }
792 return (rc == MDB_SUCCESS);
793 }
794
795 /**
796 * @retval true if the key/value pair was removed
797 * @retval false if the key wasn't found
798 * @see http://symas.com/mdb/doc/group__mdb.html#gab8182f9360ea69ac0afd4a4eaab1ddb0
799 */
800 static inline bool
801 lmdb::dbi_del(MDB_txn* const txn,
802 const MDB_dbi dbi,
803 const MDB_val* const key,
804 const MDB_val* const data = nullptr) {
805 const int rc = ::mdb_del(txn, dbi, const_cast<MDB_val*>(key), const_cast<MDB_val*>(data));
806 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
807 error::raise("mdb_del", rc);
808 }
809 return (rc == MDB_SUCCESS);
810 }
811
812 ////////////////////////////////////////////////////////////////////////////////
813 /* Procedural Interface: Cursors */
814
815 namespace lmdb {
816 static inline void cursor_open(MDB_txn* txn, MDB_dbi dbi, MDB_cursor** cursor);
817 static inline void cursor_close(MDB_cursor* cursor) noexcept;
818 static inline void cursor_renew(MDB_txn* txn, MDB_cursor* cursor);
819 static inline MDB_txn* cursor_txn(MDB_cursor* cursor) noexcept;
820 static inline MDB_dbi cursor_dbi(MDB_cursor* cursor) noexcept;
821 static inline bool cursor_get(MDB_cursor* cursor, MDB_val* key, MDB_val* data, MDB_cursor_op op);
822 static inline void cursor_put(MDB_cursor* cursor, MDB_val* key, MDB_val* data, unsigned int flags);
823 static inline void cursor_del(MDB_cursor* cursor, unsigned int flags);
824 static inline void cursor_count(MDB_cursor* cursor, std::size_t& count);
825 }
826
827 /**
828 * @throws lmdb::error on failure
829 * @see http://symas.com/mdb/doc/group__mdb.html#ga9ff5d7bd42557fd5ee235dc1d62613aa
830 */
831 static inline void
cursor_open(MDB_txn * const txn,const MDB_dbi dbi,MDB_cursor ** const cursor)832 lmdb::cursor_open(MDB_txn* const txn,
833 const MDB_dbi dbi,
834 MDB_cursor** const cursor) {
835 const int rc = ::mdb_cursor_open(txn, dbi, cursor);
836 if (rc != MDB_SUCCESS) {
837 error::raise("mdb_cursor_open", rc);
838 }
839 }
840
841 /**
842 * @see http://symas.com/mdb/doc/group__mdb.html#gad685f5d73c052715c7bd859cc4c05188
843 */
844 static inline void
cursor_close(MDB_cursor * const cursor)845 lmdb::cursor_close(MDB_cursor* const cursor) noexcept {
846 ::mdb_cursor_close(cursor);
847 }
848
849 /**
850 * @throws lmdb::error on failure
851 * @see http://symas.com/mdb/doc/group__mdb.html#gac8b57befb68793070c85ea813df481af
852 */
853 static inline void
cursor_renew(MDB_txn * const txn,MDB_cursor * const cursor)854 lmdb::cursor_renew(MDB_txn* const txn,
855 MDB_cursor* const cursor) {
856 const int rc = ::mdb_cursor_renew(txn, cursor);
857 if (rc != MDB_SUCCESS) {
858 error::raise("mdb_cursor_renew", rc);
859 }
860 }
861
862 /**
863 * @see http://symas.com/mdb/doc/group__mdb.html#ga7bf0d458f7f36b5232fcb368ebda79e0
864 */
865 static inline MDB_txn*
cursor_txn(MDB_cursor * const cursor)866 lmdb::cursor_txn(MDB_cursor* const cursor) noexcept {
867 return ::mdb_cursor_txn(cursor);
868 }
869
870 /**
871 * @see http://symas.com/mdb/doc/group__mdb.html#ga2f7092cf70ee816fb3d2c3267a732372
872 */
873 static inline MDB_dbi
cursor_dbi(MDB_cursor * const cursor)874 lmdb::cursor_dbi(MDB_cursor* const cursor) noexcept {
875 return ::mdb_cursor_dbi(cursor);
876 }
877
878 /**
879 * @throws lmdb::error on failure
880 * @see http://symas.com/mdb/doc/group__mdb.html#ga48df35fb102536b32dfbb801a47b4cb0
881 */
882 static inline bool
cursor_get(MDB_cursor * const cursor,MDB_val * const key,MDB_val * const data,const MDB_cursor_op op)883 lmdb::cursor_get(MDB_cursor* const cursor,
884 MDB_val* const key,
885 MDB_val* const data,
886 const MDB_cursor_op op) {
887 const int rc = ::mdb_cursor_get(cursor, key, data, op);
888 if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND) {
889 error::raise("mdb_cursor_get", rc);
890 }
891 return (rc == MDB_SUCCESS);
892 }
893
894 /**
895 * @throws lmdb::error on failure
896 * @see http://symas.com/mdb/doc/group__mdb.html#ga1f83ccb40011837ff37cc32be01ad91e
897 */
898 static inline void
899 lmdb::cursor_put(MDB_cursor* const cursor,
900 MDB_val* const key,
901 MDB_val* const data,
902 const unsigned int flags = 0) {
903 const int rc = ::mdb_cursor_put(cursor, key, data, flags);
904 if (rc != MDB_SUCCESS) {
905 error::raise("mdb_cursor_put", rc);
906 }
907 }
908
909 /**
910 * @throws lmdb::error on failure
911 * @see http://symas.com/mdb/doc/group__mdb.html#ga26a52d3efcfd72e5bf6bd6960bf75f95
912 */
913 static inline void
914 lmdb::cursor_del(MDB_cursor* const cursor,
915 const unsigned int flags = 0) {
916 const int rc = ::mdb_cursor_del(cursor, flags);
917 if (rc != MDB_SUCCESS) {
918 error::raise("mdb_cursor_del", rc);
919 }
920 }
921
922 /**
923 * @throws lmdb::error on failure
924 * @see http://symas.com/mdb/doc/group__mdb.html#ga4041fd1e1862c6b7d5f10590b86ffbe2
925 */
926 static inline void
cursor_count(MDB_cursor * const cursor,std::size_t & count)927 lmdb::cursor_count(MDB_cursor* const cursor,
928 std::size_t& count) {
929 const int rc = ::mdb_cursor_count(cursor, &count);
930 if (rc != MDB_SUCCESS) {
931 error::raise("mdb_cursor_count", rc);
932 }
933 }
934
935 ////////////////////////////////////////////////////////////////////////////////
936 /* Resource Interface: Values */
937
938 namespace lmdb {
939 class val;
940 }
941
942 /**
943 * Wrapper class for `MDB_val` structures.
944 *
945 * @note Instances of this class are movable and copyable both.
946 * @see http://symas.com/mdb/doc/group__mdb.html#structMDB__val
947 */
948 class lmdb::val {
949 protected:
950 MDB_val _val;
951
952 public:
953 /**
954 * Default constructor.
955 */
956 val() noexcept = default;
957
958 /**
959 * Constructor.
960 */
val(const std::string & data)961 val(const std::string& data) noexcept
962 : val{data.data(), data.size()} {}
963
964 /**
965 * Constructor.
966 */
val(const char * const data)967 val(const char* const data) noexcept
968 : val{data, std::strlen(data)} {}
969
970 /**
971 * Constructor.
972 */
val(const void * const data,const std::size_t size)973 val(const void* const data,
974 const std::size_t size) noexcept
975 : _val{size, const_cast<void*>(data)} {}
976
977 /**
978 * Move constructor.
979 */
980 val(val&& other) noexcept = default;
981
982 /**
983 * Move assignment operator.
984 */
985 val& operator=(val&& other) noexcept = default;
986
987 /**
988 * Destructor.
989 */
990 ~val() noexcept = default;
991
992 /**
993 * Returns an `MDB_val*` pointer.
994 */
995 operator MDB_val*() noexcept {
996 return &_val;
997 }
998
999 /**
1000 * Returns an `MDB_val*` pointer.
1001 */
1002 operator const MDB_val*() const noexcept {
1003 return &_val;
1004 }
1005
1006 /**
1007 * Determines whether this value is empty.
1008 */
empty()1009 bool empty() const noexcept {
1010 return size() == 0;
1011 }
1012
1013 /**
1014 * Returns the size of the data.
1015 */
size()1016 std::size_t size() const noexcept {
1017 return _val.mv_size;
1018 }
1019
1020 /**
1021 * Returns a pointer to the data.
1022 */
1023 template<typename T>
data()1024 T* data() noexcept {
1025 return reinterpret_cast<T*>(_val.mv_data);
1026 }
1027
1028 /**
1029 * Returns a pointer to the data.
1030 */
1031 template<typename T>
data()1032 const T* data() const noexcept {
1033 return reinterpret_cast<T*>(_val.mv_data);
1034 }
1035
1036 /**
1037 * Returns a pointer to the data.
1038 */
data()1039 char* data() noexcept {
1040 return reinterpret_cast<char*>(_val.mv_data);
1041 }
1042
1043 /**
1044 * Returns a pointer to the data.
1045 */
data()1046 const char* data() const noexcept {
1047 return reinterpret_cast<char*>(_val.mv_data);
1048 }
1049
1050 /**
1051 * Assigns the value.
1052 */
1053 template<typename T>
assign(const T * const data,const std::size_t size)1054 val& assign(const T* const data,
1055 const std::size_t size) noexcept {
1056 _val.mv_size = size;
1057 _val.mv_data = const_cast<void*>(reinterpret_cast<const void*>(data));
1058 return *this;
1059 }
1060
1061 /**
1062 * Assigns the value.
1063 */
assign(const char * const data)1064 val& assign(const char* const data) noexcept {
1065 return assign(data, std::strlen(data));
1066 }
1067
1068 /**
1069 * Assigns the value.
1070 */
assign(const std::string & data)1071 val& assign(const std::string& data) noexcept {
1072 return assign(data.data(), data.size());
1073 }
1074 };
1075
1076 #if !(defined(__COVERITY__) || defined(_MSC_VER))
1077 static_assert(std::is_pod<lmdb::val>::value, "lmdb::val must be a POD type");
1078 static_assert(sizeof(lmdb::val) == sizeof(MDB_val), "sizeof(lmdb::val) != sizeof(MDB_val)");
1079 #endif
1080
1081 ////////////////////////////////////////////////////////////////////////////////
1082 /* Resource Interface: Environment */
1083
1084 namespace lmdb {
1085 class env;
1086 }
1087
1088 /**
1089 * Resource class for `MDB_env*` handles.
1090 *
1091 * @note Instances of this class are movable, but not copyable.
1092 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__env
1093 */
1094 class lmdb::env {
1095 protected:
1096 MDB_env* _handle{nullptr};
1097
1098 public:
1099 static constexpr unsigned int default_flags = 0;
1100 static constexpr mode default_mode = 0644; /* -rw-r--r-- */
1101
1102 /**
1103 * Creates a new LMDB environment.
1104 *
1105 * @param flags
1106 * @throws lmdb::error on failure
1107 */
1108 static env create(const unsigned int flags = default_flags) {
1109 MDB_env* handle{nullptr};
1110 lmdb::env_create(&handle);
1111 #ifdef LMDBXX_DEBUG
1112 assert(handle != nullptr);
1113 #endif
1114 if (flags) {
1115 try {
1116 lmdb::env_set_flags(handle, flags);
1117 }
catch(const lmdb::error &)1118 catch (const lmdb::error&) {
1119 lmdb::env_close(handle);
1120 throw;
1121 }
1122 }
1123 return env{handle};
1124 }
1125
1126 /**
1127 * Constructor.
1128 *
1129 * @param handle a valid `MDB_env*` handle
1130 */
env(MDB_env * const handle)1131 env(MDB_env* const handle) noexcept
1132 : _handle{handle} {}
1133
1134 /**
1135 * Move constructor.
1136 */
env(env && other)1137 env(env&& other) noexcept {
1138 std::swap(_handle, other._handle);
1139 }
1140
1141 /**
1142 * Move assignment operator.
1143 */
1144 env& operator=(env&& other) noexcept {
1145 if (this != &other) {
1146 std::swap(_handle, other._handle);
1147 }
1148 return *this;
1149 }
1150
1151 /**
1152 * Destructor.
1153 */
~env()1154 ~env() noexcept {
1155 try { close(); } catch (...) {}
1156 }
1157
1158 /**
1159 * Returns the underlying `MDB_env*` handle.
1160 */
1161 operator MDB_env*() const noexcept {
1162 return _handle;
1163 }
1164
1165 /**
1166 * Returns the underlying `MDB_env*` handle.
1167 */
handle()1168 MDB_env* handle() const noexcept {
1169 return _handle;
1170 }
1171
1172 /**
1173 * Flushes data buffers to disk.
1174 *
1175 * @param force
1176 * @throws lmdb::error on failure
1177 */
1178 void sync(const bool force = true) {
1179 lmdb::env_sync(handle(), force);
1180 }
1181
1182 /**
1183 * Closes this environment, releasing the memory map.
1184 *
1185 * @note this method is idempotent
1186 * @post `handle() == nullptr`
1187 */
close()1188 void close() noexcept {
1189 if (handle()) {
1190 lmdb::env_close(handle());
1191 _handle = nullptr;
1192 }
1193 }
1194
1195 /**
1196 * Opens this environment.
1197 *
1198 * @param path
1199 * @param flags
1200 * @param mode
1201 * @throws lmdb::error on failure
1202 */
1203 env& open(const char* const path,
1204 const unsigned int flags = default_flags,
1205 const mode mode = default_mode) {
1206 lmdb::env_open(handle(), path, flags, mode);
1207 return *this;
1208 }
1209
1210 /**
1211 * @param flags
1212 * @param onoff
1213 * @throws lmdb::error on failure
1214 */
1215 env& set_flags(const unsigned int flags,
1216 const bool onoff = true) {
1217 lmdb::env_set_flags(handle(), flags, onoff);
1218 return *this;
1219 }
1220
1221 /**
1222 * @param size
1223 * @throws lmdb::error on failure
1224 */
set_mapsize(const std::size_t size)1225 env& set_mapsize(const std::size_t size) {
1226 lmdb::env_set_mapsize(handle(), size);
1227 return *this;
1228 }
1229
1230 /**
1231 * @param count
1232 * @throws lmdb::error on failure
1233 */
set_max_readers(const unsigned int count)1234 env& set_max_readers(const unsigned int count) {
1235 lmdb::env_set_max_readers(handle(), count);
1236 return *this;
1237 }
1238
1239 /**
1240 * @param count
1241 * @throws lmdb::error on failure
1242 */
set_max_dbs(const MDB_dbi count)1243 env& set_max_dbs(const MDB_dbi count) {
1244 lmdb::env_set_max_dbs(handle(), count);
1245 return *this;
1246 }
1247 };
1248
1249 ////////////////////////////////////////////////////////////////////////////////
1250 /* Resource Interface: Transactions */
1251
1252 namespace lmdb {
1253 class txn;
1254 }
1255
1256 /**
1257 * Resource class for `MDB_txn*` handles.
1258 *
1259 * @note Instances of this class are movable, but not copyable.
1260 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__txn
1261 */
1262 class lmdb::txn {
1263 protected:
1264 MDB_txn* _handle{nullptr};
1265
1266 public:
1267 static constexpr unsigned int default_flags = 0;
1268
1269 /**
1270 * Creates a new LMDB transaction.
1271 *
1272 * @param env the environment handle
1273 * @param parent
1274 * @param flags
1275 * @throws lmdb::error on failure
1276 */
1277 static txn begin(MDB_env* const env,
1278 MDB_txn* const parent = nullptr,
1279 const unsigned int flags = default_flags) {
1280 MDB_txn* handle{nullptr};
1281 lmdb::txn_begin(env, parent, flags, &handle);
1282 #ifdef LMDBXX_DEBUG
1283 assert(handle != nullptr);
1284 #endif
1285 return txn{handle};
1286 }
1287
1288 /**
1289 * Constructor.
1290 *
1291 * @param handle a valid `MDB_txn*` handle
1292 */
txn(MDB_txn * const handle)1293 txn(MDB_txn* const handle) noexcept
1294 : _handle{handle} {}
1295
1296 /**
1297 * Move constructor.
1298 */
txn(txn && other)1299 txn(txn&& other) noexcept {
1300 std::swap(_handle, other._handle);
1301 }
1302
1303 /**
1304 * Move assignment operator.
1305 */
1306 txn& operator=(txn&& other) noexcept {
1307 if (this != &other) {
1308 std::swap(_handle, other._handle);
1309 }
1310 return *this;
1311 }
1312
1313 /**
1314 * Destructor.
1315 */
~txn()1316 ~txn() noexcept {
1317 if (_handle) {
1318 try { abort(); } catch (...) {}
1319 _handle = nullptr;
1320 }
1321 }
1322
1323 /**
1324 * Returns the underlying `MDB_txn*` handle.
1325 */
1326 operator MDB_txn*() const noexcept {
1327 return _handle;
1328 }
1329
1330 /**
1331 * Returns the underlying `MDB_txn*` handle.
1332 */
handle()1333 MDB_txn* handle() const noexcept {
1334 return _handle;
1335 }
1336
1337 /**
1338 * Returns the transaction's `MDB_env*` handle.
1339 */
env()1340 MDB_env* env() const noexcept {
1341 return lmdb::txn_env(handle());
1342 }
1343
1344 /**
1345 * Commits this transaction.
1346 *
1347 * @throws lmdb::error on failure
1348 * @post `handle() == nullptr`
1349 */
commit()1350 void commit() {
1351 lmdb::txn_commit(_handle);
1352 _handle = nullptr;
1353 }
1354
1355 /**
1356 * Aborts this transaction.
1357 *
1358 * @post `handle() == nullptr`
1359 */
abort()1360 void abort() noexcept {
1361 lmdb::txn_abort(_handle);
1362 _handle = nullptr;
1363 }
1364
1365 /**
1366 * Resets this read-only transaction.
1367 */
reset()1368 void reset() noexcept {
1369 lmdb::txn_reset(_handle);
1370 }
1371
1372 /**
1373 * Renews this read-only transaction.
1374 *
1375 * @throws lmdb::error on failure
1376 */
renew()1377 void renew() {
1378 lmdb::txn_renew(_handle);
1379 }
1380 };
1381
1382 ////////////////////////////////////////////////////////////////////////////////
1383 /* Resource Interface: Databases */
1384
1385 namespace lmdb {
1386 class dbi;
1387 }
1388
1389 /**
1390 * Resource class for `MDB_dbi` handles.
1391 *
1392 * @note Instances of this class are movable, but not copyable.
1393 * @see http://symas.com/mdb/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b
1394 */
1395 class lmdb::dbi {
1396 protected:
1397 MDB_dbi _handle{0};
1398
1399 public:
1400 static constexpr unsigned int default_flags = 0;
1401 static constexpr unsigned int default_put_flags = 0;
1402
1403 /**
1404 * Opens a database handle.
1405 *
1406 * @param txn the transaction handle
1407 * @param name
1408 * @param flags
1409 * @throws lmdb::error on failure
1410 */
1411 static dbi
1412 open(MDB_txn* const txn,
1413 const char* const name = nullptr,
1414 const unsigned int flags = default_flags) {
1415 MDB_dbi handle{};
1416 lmdb::dbi_open(txn, name, flags, &handle);
1417 return dbi{handle};
1418 }
1419
1420 /**
1421 * Constructor.
1422 *
1423 * @param handle a valid `MDB_dbi` handle
1424 */
dbi(const MDB_dbi handle)1425 dbi(const MDB_dbi handle) noexcept
1426 : _handle{handle} {}
1427
1428 /**
1429 * Move constructor.
1430 */
dbi(dbi && other)1431 dbi(dbi&& other) noexcept {
1432 std::swap(_handle, other._handle);
1433 }
1434
1435 /**
1436 * Move assignment operator.
1437 */
1438 dbi& operator=(dbi&& other) noexcept {
1439 if (this != &other) {
1440 std::swap(_handle, other._handle);
1441 }
1442 return *this;
1443 }
1444
1445 /**
1446 * Destructor.
1447 */
~dbi()1448 ~dbi() noexcept {
1449 if (_handle) {
1450 /* No need to call close() here. */
1451 }
1452 }
1453
1454 /**
1455 * Returns the underlying `MDB_dbi` handle.
1456 */
MDB_dbi()1457 operator MDB_dbi() const noexcept {
1458 return _handle;
1459 }
1460
1461 /**
1462 * Returns the underlying `MDB_dbi` handle.
1463 */
handle()1464 MDB_dbi handle() const noexcept {
1465 return _handle;
1466 }
1467
1468 /**
1469 * Returns statistics for this database.
1470 *
1471 * @param txn a transaction handle
1472 * @throws lmdb::error on failure
1473 */
stat(MDB_txn * const txn)1474 MDB_stat stat(MDB_txn* const txn) const {
1475 MDB_stat result;
1476 lmdb::dbi_stat(txn, handle(), &result);
1477 return result;
1478 }
1479
1480 /**
1481 * Retrieves the flags for this database handle.
1482 *
1483 * @param txn a transaction handle
1484 * @throws lmdb::error on failure
1485 */
flags(MDB_txn * const txn)1486 unsigned int flags(MDB_txn* const txn) const {
1487 unsigned int result{};
1488 lmdb::dbi_flags(txn, handle(), &result);
1489 return result;
1490 }
1491
1492 /**
1493 * Returns the number of records in this database.
1494 *
1495 * @param txn a transaction handle
1496 * @throws lmdb::error on failure
1497 */
size(MDB_txn * const txn)1498 std::size_t size(MDB_txn* const txn) const {
1499 return stat(txn).ms_entries;
1500 }
1501
1502 /**
1503 * @param txn a transaction handle
1504 * @param del
1505 * @throws lmdb::error on failure
1506 */
1507 void drop(MDB_txn* const txn,
1508 const bool del = false) {
1509 lmdb::dbi_drop(txn, handle(), del);
1510 }
1511
1512 /**
1513 * Sets a custom key comparison function for this database.
1514 *
1515 * @param txn a transaction handle
1516 * @param cmp the comparison function
1517 * @throws lmdb::error on failure
1518 */
1519 dbi& set_compare(MDB_txn* const txn,
1520 MDB_cmp_func* const cmp = nullptr) {
1521 lmdb::dbi_set_compare(txn, handle(), cmp);
1522 return *this;
1523 }
1524
1525 /**
1526 * Retrieves a key/value pair from this database.
1527 *
1528 * @param txn a transaction handle
1529 * @param key
1530 * @param data
1531 * @throws lmdb::error on failure
1532 */
get(MDB_txn * const txn,const val & key,val & data)1533 bool get(MDB_txn* const txn,
1534 const val& key,
1535 val& data) {
1536 return lmdb::dbi_get(txn, handle(), key, data);
1537 }
1538
1539 /**
1540 * Retrieves a key from this database.
1541 *
1542 * @param txn a transaction handle
1543 * @param key
1544 * @throws lmdb::error on failure
1545 */
1546 template<typename K>
get(MDB_txn * const txn,const K & key)1547 bool get(MDB_txn* const txn,
1548 const K& key) const {
1549 const lmdb::val k{&key, sizeof(K)};
1550 lmdb::val v{};
1551 return lmdb::dbi_get(txn, handle(), k, v);
1552 }
1553
1554 /**
1555 * Retrieves a key/value pair from this database.
1556 *
1557 * @param txn a transaction handle
1558 * @param key
1559 * @param val
1560 * @throws lmdb::error on failure
1561 */
1562 template<typename K, typename V>
get(MDB_txn * const txn,const K & key,V & val)1563 bool get(MDB_txn* const txn,
1564 const K& key,
1565 V& val) const {
1566 const lmdb::val k{&key, sizeof(K)};
1567 lmdb::val v{};
1568 const bool result = lmdb::dbi_get(txn, handle(), k, v);
1569 if (result) {
1570 val = *v.data<const V>();
1571 }
1572 return result;
1573 }
1574
1575 /**
1576 * Retrieves a key/value pair from this database.
1577 *
1578 * @param txn a transaction handle
1579 * @param key a NUL-terminated string key
1580 * @param val
1581 * @throws lmdb::error on failure
1582 */
1583 template<typename V>
get(MDB_txn * const txn,const char * const key,V & val)1584 bool get(MDB_txn* const txn,
1585 const char* const key,
1586 V& val) const {
1587 const lmdb::val k{key, std::strlen(key)};
1588 lmdb::val v{};
1589 const bool result = lmdb::dbi_get(txn, handle(), k, v);
1590 if (result) {
1591 val = *v.data<const V>();
1592 }
1593 return result;
1594 }
1595
1596 /**
1597 * Stores a key/value pair into this database.
1598 *
1599 * @param txn a transaction handle
1600 * @param key
1601 * @param data
1602 * @param flags
1603 * @throws lmdb::error on failure
1604 */
1605 bool put(MDB_txn* const txn,
1606 const val& key,
1607 val& data,
1608 const unsigned int flags = default_put_flags) {
1609 return lmdb::dbi_put(txn, handle(), key, data, flags);
1610 }
1611
1612 /**
1613 * Stores a key into this database.
1614 *
1615 * @param txn a transaction handle
1616 * @param key
1617 * @param flags
1618 * @throws lmdb::error on failure
1619 */
1620 template<typename K>
1621 bool put(MDB_txn* const txn,
1622 const K& key,
1623 const unsigned int flags = default_put_flags) {
1624 const lmdb::val k{&key, sizeof(K)};
1625 lmdb::val v{};
1626 return lmdb::dbi_put(txn, handle(), k, v, flags);
1627 }
1628
1629 /**
1630 * Stores a key/value pair into this database.
1631 *
1632 * @param txn a transaction handle
1633 * @param key
1634 * @param val
1635 * @param flags
1636 * @throws lmdb::error on failure
1637 */
1638 template<typename K, typename V>
1639 bool put(MDB_txn* const txn,
1640 const K& key,
1641 const V& val,
1642 const unsigned int flags = default_put_flags) {
1643 const lmdb::val k{&key, sizeof(K)};
1644 lmdb::val v{&val, sizeof(V)};
1645 return lmdb::dbi_put(txn, handle(), k, v, flags);
1646 }
1647
1648 /**
1649 * Stores a key/value pair into this database.
1650 *
1651 * @param txn a transaction handle
1652 * @param key a NUL-terminated string key
1653 * @param val
1654 * @param flags
1655 * @throws lmdb::error on failure
1656 */
1657 template<typename V>
1658 bool put(MDB_txn* const txn,
1659 const char* const key,
1660 const V& val,
1661 const unsigned int flags = default_put_flags) {
1662 const lmdb::val k{key, std::strlen(key)};
1663 lmdb::val v{&val, sizeof(V)};
1664 return lmdb::dbi_put(txn, handle(), k, v, flags);
1665 }
1666
1667 /**
1668 * Stores a key/value pair into this database.
1669 *
1670 * @param txn a transaction handle
1671 * @param key a NUL-terminated string key
1672 * @param val a NUL-terminated string key
1673 * @param flags
1674 * @throws lmdb::error on failure
1675 */
1676 bool put(MDB_txn* const txn,
1677 const char* const key,
1678 const char* const val,
1679 const unsigned int flags = default_put_flags) {
1680 const lmdb::val k{key, std::strlen(key)};
1681 lmdb::val v{val, std::strlen(val)};
1682 return lmdb::dbi_put(txn, handle(), k, v, flags);
1683 }
1684
1685 /**
1686 * Removes a key/value pair from this database.
1687 *
1688 * @param txn a transaction handle
1689 * @param key
1690 * @throws lmdb::error on failure
1691 */
del(MDB_txn * const txn,const val & key)1692 bool del(MDB_txn* const txn,
1693 const val& key) {
1694 return lmdb::dbi_del(txn, handle(), key);
1695 }
1696
1697 /**
1698 * Removes a key/value pair from this database.
1699 *
1700 * @param txn a transaction handle
1701 * @param key
1702 * @throws lmdb::error on failure
1703 */
1704 template<typename K>
del(MDB_txn * const txn,const K & key)1705 bool del(MDB_txn* const txn,
1706 const K& key) {
1707 const lmdb::val k{&key, sizeof(K)};
1708 return lmdb::dbi_del(txn, handle(), k);
1709 }
1710 };
1711
1712 ////////////////////////////////////////////////////////////////////////////////
1713 /* Resource Interface: Cursors */
1714
1715 namespace lmdb {
1716 class cursor;
1717 }
1718
1719 /**
1720 * Resource class for `MDB_cursor*` handles.
1721 *
1722 * @note Instances of this class are movable, but not copyable.
1723 * @see http://symas.com/mdb/doc/group__internal.html#structMDB__cursor
1724 */
1725 class lmdb::cursor {
1726 protected:
1727 MDB_cursor* _handle{nullptr};
1728
1729 public:
1730 static constexpr unsigned int default_flags = 0;
1731
1732 /**
1733 * Creates an LMDB cursor.
1734 *
1735 * @param txn the transaction handle
1736 * @param dbi the database handle
1737 * @throws lmdb::error on failure
1738 */
1739 static cursor
open(MDB_txn * const txn,const MDB_dbi dbi)1740 open(MDB_txn* const txn,
1741 const MDB_dbi dbi) {
1742 MDB_cursor* handle{};
1743 lmdb::cursor_open(txn, dbi, &handle);
1744 #ifdef LMDBXX_DEBUG
1745 assert(handle != nullptr);
1746 #endif
1747 return cursor{handle};
1748 }
1749
1750 /**
1751 * Constructor.
1752 *
1753 * @param handle a valid `MDB_cursor*` handle
1754 */
cursor(MDB_cursor * const handle)1755 cursor(MDB_cursor* const handle) noexcept
1756 : _handle{handle} {}
1757
1758 /**
1759 * Move constructor.
1760 */
cursor(cursor && other)1761 cursor(cursor&& other) noexcept {
1762 std::swap(_handle, other._handle);
1763 }
1764
1765 /**
1766 * Move assignment operator.
1767 */
1768 cursor& operator=(cursor&& other) noexcept {
1769 if (this != &other) {
1770 std::swap(_handle, other._handle);
1771 }
1772 return *this;
1773 }
1774
1775 /**
1776 * Destructor.
1777 */
~cursor()1778 ~cursor() noexcept {
1779 try { close(); } catch (...) {}
1780 }
1781
1782 /**
1783 * Returns the underlying `MDB_cursor*` handle.
1784 */
1785 operator MDB_cursor*() const noexcept {
1786 return _handle;
1787 }
1788
1789 /**
1790 * Returns the underlying `MDB_cursor*` handle.
1791 */
handle()1792 MDB_cursor* handle() const noexcept {
1793 return _handle;
1794 }
1795
1796 /**
1797 * Closes this cursor.
1798 *
1799 * @note this method is idempotent
1800 * @post `handle() == nullptr`
1801 */
close()1802 void close() noexcept {
1803 if (_handle) {
1804 lmdb::cursor_close(_handle);
1805 _handle = nullptr;
1806 }
1807 }
1808
1809 /**
1810 * Renews this cursor.
1811 *
1812 * @param txn the transaction scope
1813 * @throws lmdb::error on failure
1814 */
renew(MDB_txn * const txn)1815 void renew(MDB_txn* const txn) {
1816 lmdb::cursor_renew(txn, handle());
1817 }
1818
1819 /**
1820 * Returns the cursor's transaction handle.
1821 */
txn()1822 MDB_txn* txn() const noexcept {
1823 return lmdb::cursor_txn(handle());
1824 }
1825
1826 /**
1827 * Returns the cursor's database handle.
1828 */
dbi()1829 MDB_dbi dbi() const noexcept {
1830 return lmdb::cursor_dbi(handle());
1831 }
1832
1833 /**
1834 * Retrieves a key from the database.
1835 *
1836 * @param key
1837 * @param op
1838 * @throws lmdb::error on failure
1839 */
get(MDB_val * const key,const MDB_cursor_op op)1840 bool get(MDB_val* const key,
1841 const MDB_cursor_op op) {
1842 return get(key, nullptr, op);
1843 }
1844
1845 /**
1846 * Retrieves a key from the database.
1847 *
1848 * @param key
1849 * @param op
1850 * @throws lmdb::error on failure
1851 */
get(lmdb::val & key,const MDB_cursor_op op)1852 bool get(lmdb::val& key,
1853 const MDB_cursor_op op) {
1854 return get(key, nullptr, op);
1855 }
1856
1857 /**
1858 * Retrieves a key/value pair from the database.
1859 *
1860 * @param key
1861 * @param val (may be `nullptr`)
1862 * @param op
1863 * @throws lmdb::error on failure
1864 */
get(MDB_val * const key,MDB_val * const val,const MDB_cursor_op op)1865 bool get(MDB_val* const key,
1866 MDB_val* const val,
1867 const MDB_cursor_op op) {
1868 return lmdb::cursor_get(handle(), key, val, op);
1869 }
1870
1871 /**
1872 * Retrieves a key/value pair from the database.
1873 *
1874 * @param key
1875 * @param val
1876 * @param op
1877 * @throws lmdb::error on failure
1878 */
get(lmdb::val & key,lmdb::val & val,const MDB_cursor_op op)1879 bool get(lmdb::val& key,
1880 lmdb::val& val,
1881 const MDB_cursor_op op) {
1882 return lmdb::cursor_get(handle(), key, val, op);
1883 }
1884
1885 /**
1886 * Retrieves a key/value pair from the database.
1887 *
1888 * @param key
1889 * @param val
1890 * @param op
1891 * @throws lmdb::error on failure
1892 */
get(std::string & key,std::string & val,const MDB_cursor_op op)1893 bool get(std::string& key,
1894 std::string& val,
1895 const MDB_cursor_op op) {
1896 lmdb::val k{}, v{};
1897 const bool found = get(k, v, op);
1898 if (found) {
1899 key.assign(k.data(), k.size());
1900 val.assign(v.data(), v.size());
1901 }
1902 return found;
1903 }
1904
1905 /**
1906 * Positions this cursor at the given key.
1907 *
1908 * @param key
1909 * @param op
1910 * @throws lmdb::error on failure
1911 */
1912 template<typename K>
1913 bool find(const K& key,
1914 const MDB_cursor_op op = MDB_SET) {
1915 lmdb::val k{&key, sizeof(K)};
1916 return get(k, nullptr, op);
1917 }
1918 };
1919
1920 ////////////////////////////////////////////////////////////////////////////////
1921
1922 #endif /* LMDBXX_H */
1923