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