1********************************
2lmdb++: a C++11 wrapper for LMDB
3********************************
4
5.. image:: https://api.travis-ci.org/bendiken/lmdbxx.svg?branch=master
6   :target: https://travis-ci.org/bendiken/lmdbxx
7   :alt: Travis CI build status
8
9.. image:: https://scan.coverity.com/projects/4900/badge.svg
10   :target: https://scan.coverity.com/projects/4900
11   :alt: Coverity Scan build status
12
13This is a comprehensive C++ wrapper for the LMDB_ embedded database library,
14offering both an error-checked procedural interface and an object-oriented
15resource interface with RAII_ semantics.
16
17.. _LMDB: http://symas.com/mdb/
18.. _RAII: http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
19
20Example
21=======
22
23Here follows a simple motivating example_ demonstrating basic use of the
24object-oriented resource interface::
25
26   #include <cstdio>
27   #include <cstdlib>
28   #include <lmdb++.h>
29
30   int main() {
31     /* Create and open the LMDB environment: */
32     auto env = lmdb::env::create();
33     env.set_mapsize(1UL * 1024UL * 1024UL * 1024UL); /* 1 GiB */
34     env.open("./example.mdb", 0, 0664);
35
36     /* Insert some key/value pairs in a write transaction: */
37     auto wtxn = lmdb::txn::begin(env);
38     auto dbi = lmdb::dbi::open(wtxn, nullptr);
39     dbi.put(wtxn, "username", "jhacker");
40     dbi.put(wtxn, "email", "jhacker@example.org");
41     dbi.put(wtxn, "fullname", "J. Random Hacker");
42     wtxn.commit();
43
44     /* Fetch key/value pairs in a read-only transaction: */
45     auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY);
46     auto cursor = lmdb::cursor::open(rtxn, dbi);
47     std::string key, value;
48     while (cursor.get(key, value, MDB_NEXT)) {
49       std::printf("key: '%s', value: '%s'\n", key.c_str(), value.c_str());
50     }
51     cursor.close();
52     rtxn.abort();
53
54     /* The enviroment is closed automatically. */
55
56     return EXIT_SUCCESS;
57   }
58
59Should any operation in the above fail, an ``lmdb::error`` exception will be
60thrown and terminate the program since we don't specify an exception handler.
61All resources will regardless get automatically cleaned up due to RAII
62semantics.
63
64.. note::
65
66   In order to run this example, you must first manually create the
67   ``./example.mdb`` directory. This is a basic characteristic of LMDB: the
68   given environment path must already exist, as LMDB will not attempt to
69   automatically create it.
70
71.. _example: https://github.com/bendiken/lmdbxx/blob/master/example.cc#L1
72
73Features
74========
75
76* Designed to be entirely self-contained as a single ``<lmdb++.h>`` header
77  file that can be dropped into a project.
78* Implements a straightforward mapping from C to C++, with consistent naming.
79* Provides both a procedural interface and an object-oriented RAII interface.
80* Simplifies error handling by translating error codes into C++ exceptions.
81* Carefully differentiates logic errors, runtime errors, and fatal errors.
82* Exception strings include the name of the LMDB function that failed.
83* Plays nice with others: all symbols are placed into the ``lmdb`` namespace.
84* 100% free and unencumbered `public domain <http://unlicense.org/>`_ software,
85  usable in any context and for any purpose.
86
87Requirements
88============
89
90The ``<lmdb++.h>`` header file requires a C++11 compiler and standard library.
91Recent releases of Clang_ or GCC_ will work fine.
92
93In addition, for your application to build and run, the underlying
94``<lmdb.h>`` header file shipped with LMDB must be available in the
95preprocessor's include path, and you must link with the ``liblmdb`` native
96library. On Ubuntu Linux 14.04 and newer, these prerequisites can be
97satisfied by installing the ``liblmdb-dev`` package.
98
99.. _Clang: http://clang.llvm.org/
100.. _GCC:   http://gcc.gnu.org/
101
102Overview
103========
104
105This wrapper offers both an error-checked procedural interface and an
106object-oriented resource interface with RAII semantics. The former will be
107useful for easily retrofitting existing projects that currently use the raw
108C interface, but we recommend the latter for all new projects due to the
109exception safety afforded by RAII semantics.
110
111Resource Interface
112------------------
113
114The high-level resource interface wraps LMDB handles in a loving RAII
115embrace. This way, you can ensure e.g. that a transaction will get
116automatically aborted when exiting a lexical scope, regardless of whether
117the escape happened normally or by throwing an exception.
118
119============================ ===================================================
120C handle                     C++ wrapper class
121============================ ===================================================
122``MDB_env*``                 ``lmdb::env``
123``MDB_txn*``                 ``lmdb::txn``
124``MDB_dbi``                  ``lmdb::dbi``
125``MDB_cursor*``              ``lmdb::cursor``
126``MDB_val``                  ``lmdb::val``
127============================ ===================================================
128
129The methods available on these C++ classes are named consistently with the
130procedural interface, below, with the obvious difference of omitting the
131handle type prefix which is already implied by the class in question.
132
133Procedural Interface
134--------------------
135
136The low-level procedural interface wraps LMDB functions with error-checking
137code that will throw an instance of a corresponding C++ exception class in
138case of failure. This interface doesn't offer any convenience overloads as
139does the resource interface; the parameter types are exactly the same as for
140the raw C interface offered by LMDB itself.  The return type is generally
141``void`` for these functions since the wrapper eats the error code returned
142by the underlying C function, throwing an exception in case of failure and
143otherwise returning values in the same output parameters as the C interface.
144
145This interface is implemented entirely using static inline functions, so
146there are no hidden extra costs to using these wrapper functions so long as
147you have a decent compiler capable of basic inlining optimization.
148
149============================ ===================================================
150C function                   C++ wrapper function
151============================ ===================================================
152``mdb_version()``            N/A
153``mdb_strerror()``           N/A
154``mdb_env_create()``         ``lmdb::env_create()``
155``mdb_env_open()``           ``lmdb::env_open()``
156``mdb_env_copy()``           ``lmdb::env_copy()``                           [1]_
157``mdb_env_copyfd()``         ``lmdb::env_copy_fd()``                        [1]_
158``mdb_env_copy2()``          ``lmdb::env_copy()``                           [1]_
159``mdb_env_copyfd2()``        ``lmdb::env_copy_fd()``                        [1]_
160``mdb_env_stat()``           ``lmdb::env_stat()``
161``mdb_env_info()``           ``lmdb::env_info()``
162``mdb_env_sync()``           ``lmdb::env_sync()``
163``mdb_env_close()``          ``lmdb::env_close()``
164``mdb_env_set_flags()``      ``lmdb::env_set_flags()``
165``mdb_env_get_flags()``      ``lmdb::env_get_flags()``
166``mdb_env_get_path()``       ``lmdb::env_get_path()``
167``mdb_env_get_fd()``         ``lmdb::env_get_fd()``
168``mdb_env_set_mapsize()``    ``lmdb::env_set_mapsize()``
169``mdb_env_set_maxreaders()`` ``lmdb::env_set_max_readers()``
170``mdb_env_get_maxreaders()`` ``lmdb::env_get_max_readers()``
171``mdb_env_set_maxdbs()``     ``lmdb::env_set_max_dbs()``
172``mdb_env_get_maxkeysize()`` ``lmdb::env_get_max_keysize()``
173``mdb_env_set_userctx()``    ``lmdb::env_set_userctx()``                    [2]_
174``mdb_env_get_userctx()``    ``lmdb::env_get_userctx()``                    [2]_
175``mdb_env_set_assert()``     N/A
176``mdb_txn_begin()``          ``lmdb::txn_begin()``
177``mdb_txn_env()``            ``lmdb::txn_env()``
178``mdb_txn_id()``             ``lmdb::txn_id()``                             [3]_
179``mdb_txn_commit()``         ``lmdb::txn_commit()``
180``mdb_txn_abort()``          ``lmdb::txn_abort()``
181``mdb_txn_reset()``          ``lmdb::txn_reset()``
182``mdb_txn_renew()``          ``lmdb::txn_renew()``
183``mdb_dbi_open()``           ``lmdb::dbi_open()``
184``mdb_stat()``               ``lmdb::dbi_stat()``                           [4]_
185``mdb_dbi_flags()``          ``lmdb::dbi_flags()``
186``mdb_dbi_close()``          ``lmdb::dbi_close()``
187``mdb_drop()``               ``lmdb::dbi_drop()``                           [4]_
188``mdb_set_compare()``        ``lmdb::dbi_set_compare()``                    [4]_
189``mdb_set_dupsort()``        ``lmdb::dbi_set_dupsort()``                    [4]_
190``mdb_set_relfunc()``        ``lmdb::dbi_set_relfunc()``                    [4]_
191``mdb_set_relctx()``         ``lmdb::dbi_set_relctx()``                     [4]_
192``mdb_get()``                ``lmdb::dbi_get()``                            [4]_
193``mdb_put()``                ``lmdb::dbi_put()``                            [4]_
194``mdb_del()``                ``lmdb::dbi_del()``                            [4]_
195``mdb_cursor_open()``        ``lmdb::cursor_open()``
196``mdb_cursor_close()``       ``lmdb::cursor_close()``
197``mdb_cursor_renew()``       ``lmdb::cursor_renew()``
198``mdb_cursor_txn()``         ``lmdb::cursor_txn()``
199``mdb_cursor_dbi()``         ``lmdb::cursor_dbi()``
200``mdb_cursor_get()``         ``lmdb::cursor_get()``
201``mdb_cursor_put()``         ``lmdb::cursor_put()``
202``mdb_cursor_del()``         ``lmdb::cursor_del()``
203``mdb_cursor_count()``       ``lmdb::cursor_count()``
204``mdb_cmp()``                N/A
205``mdb_dcmp()``               N/A
206``mdb_reader_list()``        TODO
207``mdb_reader_check()``       TODO
208============================ ===================================================
209
210.. rubric:: Footnotes
211
212.. [1] Three-parameter signature available since LMDB 0.9.14 (2014/09/20).
213
214.. [2] Only available since LMDB 0.9.11 (2014/01/15).
215
216.. [3] Only available in LMDB HEAD, not yet in any 0.9.x release (as of 0.9.16).
217       Define the ``LMDBXX_TXN_ID`` preprocessor symbol to unhide this.
218
219.. [4] Note the difference in naming. (See below.)
220
221Caveats
222^^^^^^^
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
247Error Handling
248--------------
249
250This wrapper draws a careful distinction between three different classes of
251possible LMDB error conditions:
252
253* **Logic errors**, represented by ``lmdb::logic_error``. Errors of this
254  class are thrown due to programming errors where the function interfaces
255  are used in violation of documented preconditions. A common strategy for
256  handling this class of error conditions is to abort the program with a
257  core dump, facilitating introspection to locate and remedy the bug.
258* **Fatal errors**, represented by ``lmdb::fatal_error``. Errors of this
259  class are thrown due to the exhaustion of critical system resources, in
260  particular available memory (``ENOMEM``), or due to attempts to exceed
261  applicable system resource limits. A typical strategy for handling this
262  class of error conditions is to terminate the program with a descriptive
263  error message. More robust programs and shared libraries may wish to
264  implement another strategy, such as retrying the operation after first
265  letting most of the call stack unwind in order to free up scarce
266  resources.
267* **Runtime errors**, represented by ``lmdb::runtime_error``. Errors of this
268  class are thrown as a matter of course to indicate various exceptional
269  conditions. These conditions are generally recoverable, and robust
270  programs will take care to correctly handle them.
271
272.. note::
273
274   The distinction between logic errors and runtime errors mirrors that
275   found in the C++11 standard library, where the ``<stdexcept>`` header
276   defines the standard exception base classes ``std::logic_error`` and
277   ``std::runtime_error``. The standard exception class ``std::bad_alloc``,
278   on the other hand, is a representative example of a fatal error.
279
280======================== ================================ ======================
281Error code               Exception class                  Exception type
282======================== ================================ ======================
283``MDB_KEYEXIST``         ``lmdb::key_exist_error``        runtime
284``MDB_NOTFOUND``         ``lmdb::not_found_error``        runtime
285``MDB_CORRUPTED``        ``lmdb::corrupted_error``        fatal
286``MDB_PANIC``            ``lmdb::panic_error``            fatal
287``MDB_VERSION_MISMATCH`` ``lmdb::version_mismatch_error`` fatal
288``MDB_MAP_FULL``         ``lmdb::map_full_error``         runtime
289``MDB_BAD_DBI``          ``lmdb::bad_dbi_error``          runtime           [4]_
290(others)                 ``lmdb::runtime_error``          runtime
291======================== ================================ ======================
292
293.. rubric:: Footnotes
294
295.. [4] Available since LMDB 0.9.14 (2014/09/20).
296
297.. note::
298
299   ``MDB_KEYEXIST`` and ``MDB_NOTFOUND`` are handled specially by some functions.
300
301Versioning Policy
302-----------------
303
304The lmdb++ version tracks the upstream LMDB release (x.y.z) that it is
305compatible with, and appends a sub-patch-level version (x.y.z.N) to indicate
306changes to the wrapper itself.
307
308For example, an lmdb++ release of 0.9.14.2 would indicate that it is
309designed for compatibility with LMDB 0.9.14, and is the third wrapper
310release (the first being .0, and the second .1) for that upstream target.
311
312.. note::
313
314   To the extent that LMDB will preserve API and ABI compatibility going
315   forward, older versions of the wrapper should work with newer versions of
316   LMDB; and newer versions of the wrapper will generally work with older
317   versions of LMDB by using the preprocessor to conditionalize the
318   visibility of newer symbols--see, for example, the preprocessor guards
319   around the definition of ``lmdb::env_set_userctx()``.
320
321Installation
322============
323
324lmdb++ is currently available as a package/port in the following operating
325system distributions and package management systems:
326
327================= ============== ===============================================
328Distribution      Package Name   Installation Hint
329================= ============== ===============================================
330`Arch Linux AUR`_ liblmdb++      ``yaourt -Sa liblmdb++``
331Fink_ [5]_        lmdb++         ``sudo fink install lmdb++``
332MacPorts_         lmdbxx         ``sudo port install lmdbxx``
333Portage_ [6]_     lmdb++         ``sudo emerge --ask lmdb++``
334================= ============== ===============================================
335
336.. rubric:: Footnotes
337
338.. [5] Still pending review.
339
340.. [6] Compatible with Gentoo Linux, Funtoo Linux, and Sabayon Linux.
341
342.. _Arch Linux AUR: https://aur.archlinux.org/packages/liblmdb%2B%2B/
343.. _Fink:           https://sourceforge.net/p/fink/package-submissions/4487/
344.. _MacPorts:       https://www.macports.org/ports.php?by=name&substr=lmdbxx
345.. _Portage:        https://packages.gentoo.org/package/dev-db/lmdb++
346
347Support
348=======
349
350To report a bug or submit a patch for lmdb++, please file an issue in the
351`issue tracker on GitHub <https://github.com/bendiken/lmdbxx/issues>`__.
352
353Questions and discussions about LMDB itself should be directed to the
354`OpenLDAP mailing lists <http://www.openldap.org/lists/>`__.
355
356Elsewhere
357=========
358
359Find this project at: GitHub_, Bitbucket_, `Open Hub`_, SourceForge_,
360`Travis CI`_, and `Coverity Scan`_.
361
362.. _GitHub:        https://github.com/bendiken/lmdbxx
363.. _Bitbucket:     https://bitbucket.org/bendiken/lmdbxx
364.. _Open Hub:      https://www.openhub.net/p/lmdbxx
365.. _SourceForge:   https://sourceforge.net/projects/lmdbxx/
366.. _Travis CI:     https://travis-ci.org/bendiken/lmdbxx
367.. _Coverity Scan: https://scan.coverity.com/projects/4900
368
369The API documentation is published at: http://lmdbxx.sourceforge.net/
370
371Author
372======
373
374`Arto Bendiken <https://github.com/bendiken>`_ - http://ar.to/
375
376License
377=======
378
379This is free and unencumbered public domain software. For more information,
380see http://unlicense.org/ or the accompanying ``UNLICENSE`` file.
381