Avoid overflow when running out of IDs
* Throw an exception instead * Add function that allows re-using lower IDs instead * Move functions to query IDs to read-only operations
This commit is contained in:
parent
7689f0ebd6
commit
bb985870f0
|
@ -2,15 +2,4 @@
|
|||
|
||||
namespace LMDBSafe {
|
||||
|
||||
IDType MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi)
|
||||
{
|
||||
auto cursor = txn->getRWCursor(dbi);
|
||||
MDBOutVal maxidval, maxcontent;
|
||||
auto maxid = IDType(0);
|
||||
if (!cursor.get(maxidval, maxcontent, MDB_LAST)) {
|
||||
maxid = maxidval.get<IDType>();
|
||||
}
|
||||
return maxid;
|
||||
}
|
||||
|
||||
} // namespace LMDBSafe
|
||||
|
|
|
@ -13,14 +13,6 @@ namespace LMDBSafe {
|
|||
*/
|
||||
using IDType = std::uint32_t;
|
||||
|
||||
/*!
|
||||
* \brief Returns the highest ID used in a database. Returns 0 for an empty DB.
|
||||
* \remarks
|
||||
* This makes us start everything at ID "1", which might make it possible to
|
||||
* treat id 0 as special.
|
||||
*/
|
||||
LMDB_SAFE_EXPORT IDType MDBGetMaxID(MDBRWTransaction &txn, MDBDbi &dbi);
|
||||
|
||||
/*!
|
||||
* \brief Converts \a t to an std::string.
|
||||
*
|
||||
|
@ -298,6 +290,63 @@ public:
|
|||
return stat.ms_entries;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the highest ID or 0 if the database is empty.
|
||||
*/
|
||||
IDType maxID()
|
||||
{
|
||||
auto cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
||||
MDBOutVal idval, maxcontent;
|
||||
auto id = IDType(0);
|
||||
if (!cursor.get(idval, maxcontent, MDB_LAST)) {
|
||||
id = idval.get<IDType>();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns the next highest ID in the database.
|
||||
* \remarks Never returns 0 so it can be used as special "no such ID" value.
|
||||
* \throws Throws LMDBError when running out of IDs.
|
||||
*/
|
||||
IDType nextID()
|
||||
{
|
||||
const auto id = maxID();
|
||||
if (id < std::numeric_limits<IDType>::max()) {
|
||||
return id + 1;
|
||||
}
|
||||
throw LMDBError("Running out of IDs");
|
||||
}
|
||||
|
||||
/*!
|
||||
* \brief Returns an ID not used in the database so far.
|
||||
* \remarks
|
||||
* - Lower IDs are reused but an extensive search for "gabs" is avoided.
|
||||
* - Never returns 0 so it can be used as special "no such ID" value.
|
||||
* \throws Throws LMDBError when running out of IDs.
|
||||
*/
|
||||
IDType newID()
|
||||
{
|
||||
auto cursor = (*d_parent.d_txn)->getCursor(d_parent.d_parent->d_main);
|
||||
MDBOutVal idval, maxcontent;
|
||||
auto id = IDType(1);
|
||||
if (!cursor.get(idval, maxcontent, MDB_FIRST)) {
|
||||
id = idval.get<IDType>();
|
||||
}
|
||||
if (id > 1) {
|
||||
return id - 1;
|
||||
}
|
||||
if (!cursor.get(idval, maxcontent, MDB_LAST)) {
|
||||
id = idval.get<IDType>();
|
||||
} else {
|
||||
id = 0;
|
||||
}
|
||||
if (id < std::numeric_limits<IDType>::max()) {
|
||||
return id + 1;
|
||||
}
|
||||
throw LMDBError("Running out of IDs");
|
||||
}
|
||||
|
||||
//! Get item with id, from main table directly
|
||||
bool get(IDType id, T &t)
|
||||
{
|
||||
|
@ -798,7 +847,7 @@ public:
|
|||
{
|
||||
unsigned int flags = 0;
|
||||
if (!id) {
|
||||
id = MDBGetMaxID(*d_txn, d_parent->d_main) + 1;
|
||||
id = this->nextID();
|
||||
flags = MDB_APPEND;
|
||||
}
|
||||
(*d_txn)->put(d_parent->d_main, id, serToString(t), flags);
|
||||
|
|
Loading…
Reference in New Issue