From bd089131db0174d67cff690d397c7cac74f8d685 Mon Sep 17 00:00:00 2001 From: Doug Hoyte Date: Tue, 2 Mar 2021 14:25:33 -0500 Subject: [PATCH] Allow passing std::string_view to dbi::open - Useful in case the DBI names are themselves stored in the DB. - Fixes https://github.com/hoytech/lmdbxx/issues/5 - Requested by @deepbluev7 --- README.md | 2 ++ check.cc | 36 +++++++++++++++++++++++++++++++++++- lmdb++.h | 21 +++++++++++++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8061740..0868844 100644 --- a/README.md +++ b/README.md @@ -379,6 +379,8 @@ This C++17 version is a fork of Arto Bendiken's C++11 version with the following * 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. +* `dbi::open()` now optionally accepts the DBI name as a `string_view`. This is useful when the DBI names themselves are stored in the DB. [Requested](https://github.com/hoytech/lmdbxx/issues/5) by deepbluev7. + ## Author diff --git a/check.cc b/check.cc index 0df4c35..333a9c2 100644 --- a/check.cc +++ b/check.cc @@ -16,7 +16,7 @@ int main() { lmdb::dbi mydb; lmdb::dbi mydbdups; - // Put some values in and read them back out + // Put some values in { auto txn = lmdb::txn::begin(env); @@ -28,6 +28,8 @@ int main() { txn.commit(); } + // Read them back out + { auto txn = lmdb::txn::begin(env, nullptr, MDB_RDONLY); @@ -38,6 +40,36 @@ int main() { longLivedValue = v; } + // Open DBI with a string_view + + { + std::string mydbStr = "mydbJUNK"; + + auto txn = lmdb::txn::begin(env, nullptr, MDB_RDONLY); + auto mydb2 = lmdb::dbi::open(txn, std::string_view(mydbStr).substr(0, 4)); + + std::string_view v; + mydb2.get(txn, "hello", v); + if (v != "world") throw std::runtime_error("bad read 1.5"); + } + + // Loop over null DB, to make sure it contains the table we just created + + { + auto txn = lmdb::txn::begin(env, nullptr, MDB_RDONLY); + + auto emptyDb = lmdb::dbi::open(txn, nullptr); + + auto cursor = lmdb::cursor::open(txn, emptyDb); + std::string_view key, val; + + if (!cursor.get(key, val, MDB_FIRST)) throw std::runtime_error("emptyDb cursor err 1"); + if (key != "mydb") throw std::runtime_error("emptyDb cursor err 2"); + + if (cursor.get(key, val, MDB_NEXT)) throw std::runtime_error("emptyDb cursor err 3"); + + cursor.close(); + } // Update one of the values @@ -49,6 +81,8 @@ int main() { txn.commit(); } + // Make sure updated value was stored + { auto txn = lmdb::txn::begin(env, nullptr, MDB_RDONLY); diff --git a/lmdb++.h b/lmdb++.h index fba7fcc..b954a62 100644 --- a/lmdb++.h +++ b/lmdb++.h @@ -1265,8 +1265,8 @@ class lmdb::dbi { * Opens a database handle. * * @param txn the transaction handle - * @param name - * @param flags + * @param name the database name, or nullptr + * @param flags dbi flags, ie MDB_CREATE * @throws lmdb::error on failure */ static dbi @@ -1278,6 +1278,23 @@ class lmdb::dbi { return dbi{handle}; } + /* + * This overload is so that DBI names can be passed in as string_views, which can be + * useful when storing the names of DBIs in the database itself. We need to convert it + * to a std::string to ensure that it is NUL-byte terminated. This overload comes after + * the const char* one above so that in that case there is no std::string overhead. + */ + + static dbi + open(MDB_txn* const txn, + std::string_view name, + const unsigned int flags = default_flags) { + MDB_dbi handle{}; + std::string nameStr(name); + lmdb::dbi_open(txn, nameStr.c_str(), flags, &handle); + return dbi{handle}; + } + /** * Constructor. *