• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

etc/H05-Feb-2021-

.gitignoreH A D05-Feb-2021128

.travis.ymlH A D05-Feb-20211.1 KiB

AUTHORSH A D05-Feb-202168

CREDITSH A D05-Feb-2021150

DoxyfileH A D05-Feb-2021100.6 KiB

FUNCTIONS.rstH A D05-Feb-20213.8 KiB

INSTALLH A D05-Feb-2021460

MakefileH A D05-Feb-20212.2 KiB

README.mdH A D05-Feb-202121.1 KiB

TODOH A D05-Feb-202132

UNLICENSEH A D05-Feb-20211.2 KiB

VERSIONH A D05-Feb-20216

check.ccH A D05-Feb-202110.2 KiB

example.ccH A D05-Feb-20211.8 KiB

lmdb++.hH A D05-Feb-202143.1 KiB

README.md

1# lmdb++: a C++17 wrapper for LMDB
2
3This is a comprehensive C++ wrapper for the [LMDB](http://symas.com/mdb/) embedded database library,
4offering both an error-checked procedural interface and an object-oriented
5resource interface with [RAII](http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization) semantics.
6
7This library is a fork of [Arto Bendiken](https://ar.to/)'s [lmdbxx C++11 library](https://github.com/drycpp/lmdbxx).
8The main difference from Arto's version is that the `lmdb::val` class has been removed.
9Instead, all keys and values are [std::string_view](https://en.cppreference.com/w/cpp/string/basic_string_view)s.
10See the [Fork Differences](#fork-differences) section for full details on what has been changed from Arto's version.
11
12## Example
13
14Here follows a simple motivating example demonstrating basic use of the object-oriented resource interface::
15
16    #include <iostream>
17    #include <lmdb++.h>
18
19    int main() {
20        /* Create and open the LMDB environment: */
21        auto env = lmdb::env::create();
22        env.set_mapsize(1UL * 1024UL * 1024UL * 1024UL); /* 1 GiB */
23        env.open("./example.mdb/", 0, 0664);
24        lmdb::dbi dbi;
25
26        // Get the dbi handle, and insert some key/value pairs in a write transaction:
27        {
28            auto wtxn = lmdb::txn::begin(env);
29            dbi = lmdb::dbi::open(wtxn, nullptr);
30
31            dbi.put(wtxn, "username", "jhacker");
32            dbi.put(wtxn, "email",    std::string("jhacker@example.org"));
33            dbi.put(wtxn, "fullname", std::string_view("J. Random Hacker"));
34
35            wtxn.commit();
36       }
37
38       // In a read-only transaction, get and print one of the values:
39       {
40           auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY);
41
42           std::string_view email;
43           if (dbi.get(rtxn, "email", email)) {
44               std::cout << "The email is: " << email << std::endl;
45           } else {
46               std::cout << "email not found!" << std::endl;
47           }
48       } // rtxn aborted automatically
49
50       // Print out all the values using a cursor:
51       {
52           auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY);
53
54           {
55               auto cursor = lmdb::cursor::open(rtxn, dbi);
56
57               std::string_view key, value;
58               if (cursor.get(key, value, MDB_FIRST)) {
59                   do {
60                       std::cout << "key: " << key << "  value: " << value << std::endl;
61                   } while (cursor.get(key, value, MDB_NEXT));
62               }
63           } // destroying cursor before committing/aborting transaction (see below)
64       }
65
66        return 0;
67    } // enviroment closed automatically
68
69**NOTE:** In order to run this example, you must first manually create the
70`./example.mdb/` directory. This is a basic characteristic of LMDB: the
71given environment path must already exist, as LMDB will not attempt to
72automatically create it.
73
74Should any operation in the above fail, an `lmdb::error` exception will be
75thrown and terminate the program since we don't specify an exception handler.
76All resources will regardless get automatically cleaned up due to RAII
77semantics.
78
79
80## Features
81
82* Designed to be entirely self-contained as a single `<lmdb++.h>` header file that can be dropped into a project.
83* Implements a straightforward mapping to and from the LMDB C library, with consistent naming.
84* Provides both a procedural interface and an object-oriented RAII interface.
85* Simplifies error handling by translating error codes into C++ exceptions.
86* Carefully differentiates logic errors, runtime errors, and fatal errors.
87* Exception strings include the name of the LMDB function that failed.
88* Plays nice with others: all symbols are placed into the `lmdb` namespace.
89* 100% free and unencumbered [public domain](http://unlicense.org/) software, usable in any context and for any purpose.
90
91
92## Requirements
93
94The `<lmdb++.h>` header file requires a C++17 compiler and standard library.  Recent releases of Clang or GCC will work fine.
95
96In addition, for your application to build and run, the underlying
97`<lmdb.h>` header file shipped with LMDB must be available in the
98preprocessor's include path, and you must link with the `liblmdb` native
99library. On Ubuntu Linux 14.04 and newer, these prerequisites can be
100satisfied by installing the `liblmdb-dev` package.
101
102
103
104
105## string_view
106
107LMDB uses a simple struct named `MDB_val` which contains only a `void *` and a `size_t`. This is what it uses to represent both keys and values in all functions. As of C++17, there is a standard type known as [std::string_view](https://en.cppreference.com/w/cpp/string/basic_string_view) which also contains only a pointer and a size. In the resource interface of this library, `std::string_view` is used for all keys and values.
108
109The nice aspect about `std::string_view` objects is that they are compatible with many aspects of C++. You can easily construct `std::string`s from them, ie `std::string(my_stringview)`. Unfortunately, that involves copying the data from the LMDB memory map to a new allocation on the heap (unless your string is short, then a [short string optimisation](https://stackoverflow.com/questions/21694302/what-are-the-mechanics-of-short-string-optimization-in-libc) may apply).
110
111However, with some care `std::string_view` lets you avoid copying in several cases. For example, you can take zero-copy substrings by using `substr()`. Many modern C++ libraries are now being designed to reduce or eliminate copying by accepting or returning `std::string_view` objects, for example the [TAO C++ JSON parser](https://github.com/taocpp/json) and the [flatbuffers serialisation system](http://google.github.io/flatbuffers/).
112
113With `std::string_view` the standard LMDB caveats apply: If you need to keep the data around after closing the LMDB transaction (or after performing any write operation on the DB) then you need to make a copy. This is as easy as assigning the `std::string_view` to an `std::string`.
114
115    std::string longLivedValue;
116
117    {
118        auto txn = lmdb::txn::begin(env);
119        auto mydb = lmdb::dbi::open(txn, "mydb");
120
121        std::string_view v;
122        mydb.get(txn, "hello", v);
123
124        longLivedValue = v;
125    }
126
127In the code above, note that `"hello"` was passed in as a key. This works because a `std::string_view` is implicitly constructed. This works for `char *`, `std::string`, etc.
128
129
130### string_view Conversions
131
132Arto's original version of this library had templated `get` and `put` convenience methods. These methods reduced type safety and [caused problems for some users](https://github.com/drycpp/lmdbxx/issues/1) so this fork has removed them in favour of explicit methods to convert to and from `std::string_view`s.
133
134**Note:** These conversion functions described in this section are mostly designed for storing integers in `MDB_INTEGERKEY`/`MDB_INTEGERDUP` databases. Although you can use them for more complicated types, we do not recommend doing so. Instead, please look into zero-copy serialization schemes such as [flatbuffers](https://google.github.io/flatbuffers/) or [capn proto](https://capnproto.org/). With these you can get almost all the performance benefit of storing raw structs. In addition you will get more safety, the ability to access your database from languages other than C/C++, database portability across systems, and a way to upgrade your structures by adding new fields, deprecating old ones, etc.
135
136If you do decide to store complex structs directly, you have to be very careful when using the following methods. If you have any pointers in your structures then you will almost certainly experience out-of-bounds memory accesses, and/or memory corruption.
137
138It is **strongly** recommended that you develop and test using [address sanitizer](https://en.wikipedia.org/wiki/AddressSanitizer) when working with these routines (and in general). This will help you detect problems early on during development. The `Makefile` compiles the `check.cpp` test suite with `-fsanitize=address` for this reason.
139
140#### Copying
141
142For example, suppose you want to store raw `uint64_t` values in a DB. You can use the `to_sv` function to create a `string_view` which can then be passed to a `put` method:
143
144      mydb.put(txn, "some_key", lmdb::to_sv<uint64_t>(123456));
145
146**NOTE:** The above `to_sv` call will create a `std::string_view` pointing to a temporary object. You should ensure that you don't retain the `string_view` outside of the current [full expression](https://en.cppreference.com/w/cpp/language/lifetime), which in this case is the `mydb.put()`. Otherwise, you will encounter undefined behaviour.
147
148Afterwards, you can `get` the value back out of the DB and extract the `uint64_t` with `from_sv`:
149
150      std::string_view view;
151      mydb.get(txn, "some_key", view);
152      uint64_t val = lmdb::from_sv<uint64_t>(view);
153
154This copies the memory from the database and returns this copy for you to use. In the case of simple data-types like `uint64_t` this doesn't make a difference, but for large structs you may want to use the pointer-based conversions described in the next section.
155
156`from_sv` will throw an `MDB_BAD_VALSIZE` exception if the view isn't the expected size (in this case, 8 bytes). You should also use this method if you wish to ensure that your value is correctly aligned prior to accessing it since LMDB only guarantees 2-byte alignment of keys, unless you are [careful with the sizes of your keys and data](https://www.reddit.com/r/programming/comments/1daiu8/interesting_fast_and_small_keyvalue_data_store/c9p8ml1/).
157
158#### Pointer-based
159
160If you wish to avoid the copying and have the `string_view` point directly to an existing block of memory, you can use `ptr_to_sv` (note that the templated type is optional here since it can be inferred from the pointer type):
161
162      uint64_t val = 123456;
163      mydb.put(txn, "some_key", lmdb::ptr_to_sv(&val));
164
165Note that you are responsible for managing the backing memory, and you should ensure that it is valid for as long as you need the constructed `string_view`.
166
167Similarly, you can get a pointer pointing into the LMDB mapped memory by using `ptr_from_sv`:
168
169      std::string_view view;
170      mydb.get(txn, "some_key", view);
171      uint64_t *ptr = lmdb::ptr_from_sv<uint64_t>(view);
172
173Since the returned pointer is pointing into LMDB's mapped memory, you should not use this pointer after the transaction has been terminated, or after performing any write operations on the DB.
174
175As with `from_sv`, `ptr_from_sv` will throw an `MDB_BAD_VALSIZE` exception if the view isn't the expected size (in this case, 8 bytes).
176
177
178## Interfaces
179
180This wrapper offers both an error-checked procedural interface and an
181object-oriented resource interface with RAII semantics. The former will be
182useful for easily retrofitting existing projects that currently use the raw
183C interface, but **we recommend the resource interface** for all new projects due to the
184exception safety afforded by RAII semantics.
185
186### Resource Interface
187
188The high-level resource interface wraps LMDB handles in a loving RAII
189embrace. This way, you can ensure e.g. that a transaction will get
190automatically aborted when exiting a lexical scope, regardless of whether
191the escape happened normally or by throwing an exception.
192
193| C handle       |             C++ wrapper class |
194|----------------|-------------------------------|
195|`MDB_env*`                 |`lmdb::env` |
196|`MDB_txn*`                 |`lmdb::txn` |
197|`MDB_dbi`                  |`lmdb::dbi` |
198|`MDB_cursor*`              |`lmdb::cursor` |
199|`MDB_val`                  |`std::string_view` |
200
201The methods available on these C++ classes are named consistently with the
202procedural interface, below, with the obvious difference of omitting the
203handle type prefix which is already implied by the class in question.
204
205### Procedural Interface
206
207The low-level procedural interface wraps LMDB functions with error-checking
208code that will throw an instance of a corresponding C++ exception class in
209case of failure. This interface doesn't offer any convenience overloads as
210does the resource interface; the parameter types are exactly the same as for
211the raw C interface offered by LMDB itself.  The return type is generally
212`void` for these functions since the wrapper eats the error code returned
213by the underlying C function, throwing an exception in case of failure and
214otherwise returning values in the same output parameters as the C interface.
215
216This interface is implemented entirely using static inline functions, so
217there are no hidden extra costs to using these wrapper functions so long as
218you have a decent compiler capable of basic inlining optimization.
219
220See the [FUNCTIONS.rst](FUNCTIONS.rst) file for a mapping of the procedural interface to the underlying LMDB C functions.
221
222### Caveats
223
224* The C++ procedural interface is more strictly and consistently grouped by
225  handle type than is the LMDB native interface.  For instance,
226  `mdb_put()` is wrapped as the C++ function `lmdb::dbi_put()`, not
227  `lmdb::put()`.  These differences--a handful in number--all concern
228  operations on database handles.
229
230* The C++ interface takes some care to be const-correct for input-only
231  parameters, something the original C interface largely ignores.
232  Hence occasional use of `const_cast` in the wrapper code base.
233
234* `lmdb::dbi_put()` does not throw an exception if LMDB returns the
235  `MDB_KEYEXIST` error code; it instead just returns `false`.
236  This is intended to simplify common usage patterns.
237
238* `lmdb::dbi_get()`, `lmdb::dbi_del()`, and `lmdb::cursor_get()` do
239  not throw an exception if LMDB returns the `MDB_NOTFOUND` error code;
240  they instead just return `false`.
241  This is intended to simplify common usage patterns.
242
243* `lmdb::env_get_max_keysize()` returns an unsigned integer, instead of a
244  signed integer as the underlying `mdb_env_get_maxkeysize()` function does.
245  This conversion is done since the return value cannot in fact be negative.
246
247### Cursor double-free issue
248
249In a read-write transaction, you must make sure to call `.close()` on your cursors (or let them go out of scope) **before** committing or aborting your transaction.
250Otherwise you will do a double-free which, if you are lucky, will crash your process. This is described in [this github issue](https://github.com/drycpp/lmdbxx/issues/22).
251
252Consider this code:
253
254    {
255        auto txn = lmdb::txn::begin(env);
256        auto mydb = lmdb::dbi::open(txn, "mydb");
257
258        auto cursor = lmdb::cursor::open(txn, mydb);
259        std::string_view key, val;
260        cursor.get(key, val, MDB_FIRST);
261
262        txn.commit();
263    } // <-- BAD! cursor is destroyed here
264
265The above code will result in a double free. You can uncomment a test case in `check.cc` if you want to verify this for yourself. When compiled with `-fsanitize=address` you will see the following:
266
267    ==14400==ERROR: AddressSanitizer: attempting double-free on 0x614000000240 in thread T0:
268
269To fix this, you should call `cursor.close()` before you call `txn.commit()`. Or, alternatively, do your cursor operations in a sub-scope so the cursor is destroyed before the transaction is committed:
270
271    {
272        auto txn = lmdb::txn::begin(env);
273        auto mydb = lmdb::dbi::open(txn, "mydb");
274
275        {
276            auto cursor = lmdb::cursor::open(txn, mydb);
277            std::string_view key, val;
278            cursor.get(key, val, MDB_FIRST);
279        } // <-- GOOD! cursor is destroyed here
280
281        txn.commit();
282    }
283
284Note that the double-free issue does not affect read-only transactions, but it is good practice to ensure closing/destruction of all cursors and transactions happen in the correct order, as shown in the motivating example. This is because you may change a read-only transaction to a read-write transaction in the future.
285
286
287## Error Handling
288
289This wrapper draws a careful distinction between three different classes of
290possible LMDB error conditions:
291
292* **Logic errors**, represented by `lmdb::logic_error`. Errors of this
293  class are thrown due to programming errors where the function interfaces
294  are used in violation of documented preconditions. A common strategy for
295  handling this class of error conditions is to abort the program with a
296  core dump, facilitating introspection to locate and remedy the bug.
297* **Fatal errors**, represented by `lmdb::fatal_error`. Errors of this
298  class are thrown due to the exhaustion of critical system resources, in
299  particular available memory (`ENOMEM`), or due to attempts to exceed
300  applicable system resource limits. A typical strategy for handling this
301  class of error conditions is to terminate the program with a descriptive
302  error message. More robust programs and shared libraries may wish to
303  implement another strategy, such as retrying the operation after first
304  letting most of the call stack unwind in order to free up scarce
305  resources.
306* **Runtime errors**, represented by `lmdb::runtime_error`. Errors of this
307  class are thrown as a matter of course to indicate various exceptional
308  conditions. These conditions are generally recoverable, and robust
309  programs will take care to correctly handle them.
310
311**NOTE:** The distinction between logic errors and runtime errors mirrors that
312   found in the C++11 standard library, where the `<stdexcept>` header
313   defines the standard exception base classes `std::logic_error` and
314   `std::runtime_error`. The standard exception class `std::bad_alloc`,
315   on the other hand, is a representative example of a fatal error.
316
317| Error code            |    Exception class            |      Exception type |
318|-----------------------|-------------------------------|---------------------|
319|`MDB_KEYEXIST`         |`lmdb::key_exist_error`        | runtime             |
320|`MDB_NOTFOUND`         |`lmdb::not_found_error`        | runtime             |
321|`MDB_CORRUPTED`        |`lmdb::corrupted_error`        | fatal               |
322|`MDB_PANIC`            |`lmdb::panic_error`            | fatal               |
323|`MDB_VERSION_MISMATCH` |`lmdb::version_mismatch_error` | fatal               |
324|`MDB_MAP_FULL`         |`lmdb::map_full_error`         | runtime             |
325|`MDB_BAD_DBI`          |`lmdb::bad_dbi_error`          | runtime [4]         |
326|(others)               |`lmdb::runtime_error`          | runtime             |
327
328* [4] Available since LMDB 0.9.14 (2014/09/20).
329* `MDB_KEYEXIST` and `MDB_NOTFOUND` are handled specially by some functions.
330
331
332## Support
333
334To report a bug or submit a patch for lmdb++, please file an issue in the [issue tracker on GitHub](https://github.com/hoytech/lmdbxx/issues).
335
336Questions and discussions about LMDB itself should be directed to the [OpenLDAP mailing lists](http://www.openldap.org/lists/).
337
338Also see Arto's original [github](https://github.com/bendiken/lmdbxx) (not maintained anymore?) and [sourceforge documentation](https://sourceforge.net/projects/lmdbxx/) (not up to date with this fork's changes).
339
340
341
342## Fork Differences
343
344This C++17 version is a fork of Arto Bendiken's C++11 version with the following changes:
345
346* `lmdb::val` has been removed and replaced with `std::string_view`. See the [string::view section](#string_view) for more details.
347
348* The templated versions of the `get` and `put` methods have been removed. See the conversion methods described in [string_view Conversions](#string_view-conversions) for an alternative.
349
350* Changes to cursors:
351    * The cursor interface has been completed. `put`, `del`, and `count` have been added, bringing us to parity with the LMDB API.
352    * The cursor `find` method has been removed. This method did not correspond to any function in LMDB API. All it did was a `get` with a cursor op of `MDB_SET`. You should now do this directly, and consider the differences between `MDB_SET`, `MDB_SET_KEY`, and `MDB_GET_BOTH_RANGE`.
353    * The option of passing `MDB_val*` in via the cursor resource interface has been removed. Now you must use `std::string_view`. Of course the procedural interface still lets you use raw `MDB_val*`s if you want.
354    * `cursor_put` returns `bool` to propagate the condition that the key already exists and either `MDB_NODUPDATA` or `MDB_NOOVERWRITE` were set. This makes it consistent with `cursor_get`.
355
356* A `del` method has been added to the `lmdb::dbi` resource interface that lets you pass in a value as well as a key so that you can delete sorted dup items via dbi objects.
357
358* `lmdb::dbi` instances can now be constructed uninitialized. Attempting to use them in this state will result in an error. You should initialize them first, for example:
359
360      lmdb::dbi mydb;
361
362      // mydb is uninitialized, don't use it!
363
364      {
365          auto txn = lmdb::txn::begin(env);
366          mydb = lmdb::dbi::open(txn, "mydb", MDB_CREATE);
367          txn.commit();
368      }
369
370      // now mydb is safe to use
371
372* `lmdb::dbi` instances can now be copied.
373
374* Considerably expanded the test suite.
375
376* Converted documentation to markdown.
377
378* Added a section to the docs describing the [cursor double-free issue](#cursor-double-free-issue).
379
380* If an exception was throw by `txn.commit()` (ie `MDB_MAP_FULL`), and this transaction was later aborted (because it went out of scope while unwinding the stack), then a double-free would occur. This was [fixed](https://github.com/hoytech/lmdbxx/pull/3) by Niklas Salmoukas.
381
382
383
384## Author
385
386[Arto Bendiken](https://ar.to/)
387
388This fork maintained by [Doug Hoyte](https://hoytech.com)
389
390## License
391
392This is free and unencumbered public domain software. For more information,
393see http://unlicense.org/ or the accompanying `UNLICENSE` file.
394