2023-03-23 17:27:46 +00:00
/*
* LMDB Abstraction Layer .
* Copyright ( C ) 2023 Yury Gubich < blue @ macaw . me >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2023-03-20 15:37:13 +00:00
2023-03-21 11:05:54 +00:00
# ifndef LMDBAL_STORAGE_HPP
# define LMDBAL_STORAGE_HPP
2023-03-20 15:37:13 +00:00
# include "storage.h"
# include "exceptions.h"
2023-08-09 17:41:15 +00:00
# define UNUSED(x) (void)(x)
2023-04-07 23:27:53 +00:00
/**
* \ class LMDBAL : : Storage
* \ brief This is a basic key value storage .
*
* \ tparam K type of the keys of the storage
* \ tparam V type of the values of the storage
*
* You can receive an instance of this class calling LMDBAL : : Base : : addStorage ( const std : : string & )
* if the database is yet closed and you ' re defining the storages you ' re going to need .
2023-04-12 15:36:33 +00:00
* Or you can call LMDBAL : : Base : : getStorage ( const std : : string & ) if you didn ' t save a pointer to the storage at first
2023-04-07 23:27:53 +00:00
*
* You are not supposed to instantiate or destory instances of this class yourself !
*/
2023-04-12 15:36:33 +00:00
/**
* \ brief Creates a storage
*
2023-08-15 18:48:19 +00:00
* \ param [ in ] parent - LMDBAL : : Base pointer for the owning database ( borrowed )
* \ param [ in ] name - the name of the storage
* \ param [ in ] duplicates - true if storage supports duplicates , false otherwise ( false by default )
2023-04-12 15:36:33 +00:00
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-08-15 18:48:19 +00:00
LMDBAL : : Storage < K , V > : : Storage ( Base * parent , const std : : string & name , bool duplicates ) :
iStorage ( parent , name , duplicates ) ,
2023-08-05 20:13:43 +00:00
keySerializer ( ) ,
valueSerializer ( ) ,
cursors ( )
2023-03-20 15:37:13 +00:00
{ }
2023-04-12 15:36:33 +00:00
/**
* \ brief Destroys a storage
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-08-07 21:27:44 +00:00
LMDBAL : : Storage < K , V > : : ~ Storage ( ) {
for ( Cursor < K , V > * cursor : cursors )
delete cursor ;
}
2023-03-20 15:37:13 +00:00
2023-04-07 23:27:53 +00:00
/**
* \ brief Adds a key - value record to the storage
*
* Take a note that if the storage already had a record you want to add LMDBAL : : Exist is thrown
*
* \ param [ in ] key key of the record
* \ param [ in ] value value of the record
*
* \ exception LMDBAL : : Exist thrown if the storage already has a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
void LMDBAL : : Storage < K , V > : : addRecord ( const K & key , const V & value ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( addRecordMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : addRecord ( key , value , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
commitTransaction ( txn ) ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Adds a key - value record to the storage ( transaction variant )
*
* This function schedules an addition of a key - value record , but doesn ' t immidiately adds it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
*
* Take a note that if the storage already had a record you want to add LMDBAL : : Exist is thrown
*
* \ param [ in ] key key of the record
* \ param [ in ] value value of the record
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
*
* \ exception LMDBAL : : Exist thrown if the storage already has a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : addRecord ( const K & key , const V & value , TransactionID txn ) {
ensureOpened ( addRecordMethodName ) ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
MDB_val lmdbData = valueSerializer . setData ( value ) ;
2023-03-20 15:37:13 +00:00
int rc = mdb_put ( txn , dbi , & lmdbKey , & lmdbData , MDB_NOOVERWRITE ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwDuplicateOrUnknown ( rc , toString ( key ) ) ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Adds a key - value record to the storage , overwrites if it already exists
* \ param [ in ] key key of the record
* \ param [ in ] value value of the record
* \ returns true if the record was added , false otherwise
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
bool LMDBAL : : Storage < K , V > : : forceRecord ( const K & key , const V & value ) {
ensureOpened ( forceRecordMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
bool added ;
try {
2023-04-04 23:27:31 +00:00
added = Storage < K , V > : : forceRecord ( key , value , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
2023-03-20 15:37:13 +00:00
commitTransaction ( txn ) ;
2023-03-28 20:45:35 +00:00
return added ;
2023-03-20 15:37:13 +00:00
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Adds a key - value record to the storage , overwrites if it already exists ( transaction variant )
*
* This function schedules an addition of a key - value record , but doesn ' t immidiately adds it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
* If the record did already exist in the database the actual overwrite will be done only after calling LMDBAL : : Base : : commitTransaction ( ) .
*
* \ param [ in ] key key of the record
* \ param [ in ] value value of the record
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
* \ returns true if the record was added , false otherwise
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-28 20:45:35 +00:00
bool LMDBAL : : Storage < K , V > : : forceRecord ( const K & key , const V & value , TransactionID txn ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( forceRecordMethodName ) ;
bool added ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
2023-03-20 15:37:13 +00:00
MDB_val lmdbData ;
int rc = mdb_get ( txn , dbi , & lmdbKey , & lmdbData ) ;
switch ( rc ) {
case MDB_SUCCESS :
added = false ;
break ;
case MDB_NOTFOUND :
added = true ;
break ;
default :
added = false ;
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
}
2023-08-05 20:13:43 +00:00
lmdbData = valueSerializer . setData ( value ) ;
2023-03-20 15:37:13 +00:00
rc = mdb_put ( txn , dbi , & lmdbKey , & lmdbData , 0 ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
return added ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Changes key - value record to the storage .
*
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record
* \ param [ in ] value new value of the record
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
void LMDBAL : : Storage < K , V > : : changeRecord ( const K & key , const V & value ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( changeRecordMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : changeRecord ( key , value , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
commitTransaction ( txn ) ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Changes key - value record to the storage ( transaction variant )
*
* This function schedules a modification of a key - value record , but doesn ' t immidiately changes it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
*
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record
* \ param [ in ] value new value of the record
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : changeRecord ( const K & key , const V & value , TransactionID txn ) {
ensureOpened ( changeRecordMethodName ) ;
2023-04-07 23:27:53 +00:00
MDB_cursor * cursor ;
int rc = mdb_cursor_open ( txn , dbi , & cursor ) ;
if ( rc ! = MDB_SUCCESS )
throwUnknown ( rc ) ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
2023-04-07 23:27:53 +00:00
rc = mdb_cursor_get ( cursor , & lmdbKey , nullptr , MDB_SET ) ;
if ( rc ! = MDB_SUCCESS )
throwNotFoundOrUnknown ( rc , toString ( key ) ) ;
2023-03-20 15:37:13 +00:00
2023-08-05 20:13:43 +00:00
MDB_val lmdbData = valueSerializer . setData ( value ) ;
2023-04-07 23:27:53 +00:00
rc = mdb_cursor_put ( cursor , & lmdbKey , & lmdbData , MDB_CURRENT ) ;
mdb_cursor_close ( cursor ) ;
2023-03-20 15:37:13 +00:00
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
}
2023-04-07 23:27:53 +00:00
/**
* \ brief G ets the record from the database
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record you look for
* \ returns the value from the storage
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
V LMDBAL : : Storage < K , V > : : getRecord ( const K & key ) const {
2023-03-20 15:37:13 +00:00
ensureOpened ( getRecordMethodName ) ;
2023-03-28 20:45:35 +00:00
V value ;
2023-04-04 23:27:31 +00:00
Storage < K , V > : : getRecord ( key , value ) ;
2023-03-28 20:45:35 +00:00
return value ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Gets the record from the database ( reference variant )
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record you look for
* \ param [ out ] value the value from the storage
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : getRecord ( const K & key , V & value ) const {
ensureOpened ( getRecordMethodName ) ;
2023-03-20 15:37:13 +00:00
TransactionID txn = beginReadOnlyTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : getRecord ( key , value , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
abortTransaction ( txn ) ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Gets the record from the database ( transaction variant )
*
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginReadOnlyTransaction ( ) or LMDBAL : : Base : : beginTransaction ( ) .
* If you just want to read data you should prefer LMDBAL : : Base : : beginReadOnlyTransaction ( ) .
*
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record you look for
* \ param [ in ] txn transaction ID , can be read only transaction
* \ returns the value from the storage
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
V LMDBAL : : Storage < K , V > : : getRecord ( const K & key , TransactionID txn ) const {
ensureOpened ( getRecordMethodName ) ;
V value ;
2023-04-04 23:27:31 +00:00
Storage < K , V > : : getRecord ( key , value , txn ) ;
2023-03-28 20:45:35 +00:00
return value ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Gets the record from the database ( transaction , reference variant )
*
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginReadOnlyTransaction ( ) or LMDBAL : : Base : : beginTransaction ( ) .
* If you just want to read data you should prefer LMDBAL : : Base : : beginReadOnlyTransaction ( ) .
*
* Take a note that if the storage didn ' t have a record you want to change LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record you look for
* \ param [ out ] value the value from the storage
* \ param [ in ] txn transaction ID , can be read only transaction
*
* \ exception LMDBAL : : NotFound thrown if the storage doesn ' t have a record with the given key
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : getRecord ( const K & key , V & value , TransactionID txn ) const {
ensureOpened ( getRecordMethodName ) ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
2023-03-20 15:37:13 +00:00
MDB_val lmdbData ;
int rc = mdb_get ( txn , dbi , & lmdbKey , & lmdbData ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwNotFoundOrUnknown ( rc , toString ( key ) ) ;
2023-03-20 15:37:13 +00:00
2023-08-05 20:13:43 +00:00
valueSerializer . deserialize ( lmdbData , value ) ;
2023-03-20 15:37:13 +00:00
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Chechs if storage has value
*
* \ param [ in ] key key of the record you look for
* \ returns true if there was a record with given key , false otherwise
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
bool LMDBAL : : Storage < K , V > : : checkRecord ( const K & key ) const {
2023-03-20 15:37:13 +00:00
ensureOpened ( checkRecordMethodName ) ;
TransactionID txn = beginReadOnlyTransaction ( ) ;
2023-03-28 20:45:35 +00:00
bool result ;
try {
2023-04-04 23:27:31 +00:00
result = Storage < K , V > : : checkRecord ( key , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
abortTransaction ( txn ) ;
return result ;
}
2023-04-07 23:27:53 +00:00
/**
* \ brief Chechs if storage has value ( transaction variant )
*
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginReadOnlyTransaction ( ) or LMDBAL : : Base : : beginTransaction ( ) .
* If you just want to read data you should prefer LMDBAL : : Base : : beginReadOnlyTransaction ( ) .
*
* \ param [ in ] key key of the record you look for
* \ param [ in ] txn transaction ID , can be read only transaction
* \ returns true if there was a record with given key , false otherwise
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
bool LMDBAL : : Storage < K , V > : : checkRecord ( const K & key , TransactionID txn ) const {
ensureOpened ( checkRecordMethodName ) ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
2023-03-20 15:37:13 +00:00
MDB_val lmdbData ;
int rc = mdb_get ( txn , dbi , & lmdbKey , & lmdbData ) ;
if ( rc = = MDB_SUCCESS )
return true ;
if ( rc ! = MDB_NOTFOUND )
throwUnknown ( rc ) ;
return false ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Reads whole storage into a map
*
* Basically just reads all database in an std : : map , usefull when you store small storages
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
std : : map < K , V > LMDBAL : : Storage < K , V > : : readAll ( ) const {
2023-03-20 15:37:13 +00:00
ensureOpened ( readAllMethodName ) ;
2023-03-28 20:45:35 +00:00
std : : map < K , V > result ;
2023-04-04 23:27:31 +00:00
Storage < K , V > : : readAll ( result ) ;
2023-03-28 20:45:35 +00:00
return result ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Reads whole storage into a map ( reference variant )
*
* Basically just reads all database in an std : : map , usefull when you store small storages
*
* \ param [ out ] result a map that is going to contain all data
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : readAll ( std : : map < K , V > & result ) const {
ensureOpened ( readAllMethodName ) ;
2023-03-20 15:37:13 +00:00
TransactionID txn = beginReadOnlyTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : readAll ( result , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
abortTransaction ( txn ) ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Reads whole storage into a map ( transaction variant )
*
* Basically just reads all database in an std : : map , usefull when you store small storages
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginReadOnlyTransaction ( ) or LMDBAL : : Base : : beginTransaction ( ) .
* If you just want to read data you should prefer LMDBAL : : Base : : beginReadOnlyTransaction ( ) .
*
* \ param [ in ] txn transaction ID , can be read only transaction
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
std : : map < K , V > LMDBAL : : Storage < K , V > : : readAll ( TransactionID txn ) const {
ensureOpened ( readAllMethodName ) ;
2023-03-20 15:37:13 +00:00
std : : map < K , V > result ;
2023-04-04 23:27:31 +00:00
Storage < K , V > : : readAll ( result , txn ) ;
2023-03-28 20:45:35 +00:00
return result ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Reads whole storage into a map ( transaction , reference variant )
*
* Basically just reads all database in an std : : map , usefull when you store small storages
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginReadOnlyTransaction ( ) or LMDBAL : : Base : : beginTransaction ( ) .
* If you just want to read data you should prefer LMDBAL : : Base : : beginReadOnlyTransaction ( ) .
*
* \ param [ out ] result a map that is going to contain all data
* \ param [ in ] txn transaction ID , can be read only transaction
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : readAll ( std : : map < K , V > & result , TransactionID txn ) const {
ensureOpened ( readAllMethodName ) ;
2023-03-20 15:37:13 +00:00
MDB_cursor * cursor ;
MDB_val lmdbKey , lmdbData ;
int rc = mdb_cursor_open ( txn , dbi , & cursor ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
rc = mdb_cursor_get ( cursor , & lmdbKey , & lmdbData , MDB_FIRST ) ;
while ( rc = = MDB_SUCCESS ) {
2023-03-28 20:45:35 +00:00
K key ;
2023-08-05 20:13:43 +00:00
keySerializer . deserialize ( lmdbKey , key ) ;
2023-03-28 20:45:35 +00:00
V & value = result [ key ] ;
2023-08-05 20:13:43 +00:00
valueSerializer . deserialize ( lmdbData , value ) ;
2023-03-20 15:37:13 +00:00
rc = mdb_cursor_get ( cursor , & lmdbKey , & lmdbData , MDB_NEXT ) ;
}
2023-04-07 23:27:53 +00:00
mdb_cursor_close ( cursor ) ;
2023-03-20 15:37:13 +00:00
if ( rc ! = MDB_NOTFOUND )
throwUnknown ( rc ) ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Replaces the content of the whole storage with the given
*
* Basically this function drops the database and adds all the records from the given map
*
* \ param [ in ] data new data of the storage
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
void LMDBAL : : Storage < K , V > : : replaceAll ( const std : : map < K , V > & data ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( replaceAllMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : replaceAll ( data , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
commitTransaction ( txn ) ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Replaces the content of the whole storage with the given ( transaction variant )
*
* Basically this function drops the database and adds all the records from the given map
* This function schedules a data replacement , but doesn ' t immidiately execute it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
*
* \ param [ in ] data new data of the storage
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : replaceAll ( const std : : map < K , V > & data , TransactionID txn ) {
ensureOpened ( replaceAllMethodName ) ;
2023-03-20 15:37:13 +00:00
int rc = drop ( txn ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
MDB_val lmdbKey , lmdbData ;
for ( const std : : pair < const K , V > & pair : data ) {
2023-08-05 20:13:43 +00:00
lmdbKey = keySerializer . setData ( pair . first ) ;
lmdbData = valueSerializer . setData ( pair . second ) ;
2023-03-20 15:37:13 +00:00
2023-04-09 17:19:23 +00:00
rc = mdb_put ( txn , dbi , & lmdbKey , & lmdbData , MDB_NOOVERWRITE ) ; //TODO may be appending with cursor makes sence here?
2023-03-20 15:37:13 +00:00
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
}
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Adds records in bulk
*
* \ param [ in ] data the data to be added
* \ param [ in ] overwrite if false function throws LMDBAL : : Exist on repeated key , if true - overwrites it
* \ returns new actual amount of records in the storage
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Exist thrown if overwrite = = false and at least one of the keys of data already exists in the storage
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
uint32_t LMDBAL : : Storage < K , V > : : addRecords ( const std : : map < K , V > & data , bool overwrite ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( addRecordsMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
2023-03-28 20:45:35 +00:00
uint32_t amount ;
try {
2023-04-04 23:27:31 +00:00
amount = Storage < K , V > : : addRecords ( data , txn , overwrite ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
commitTransaction ( txn ) ;
return amount ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Adds records in bulk ( transaction variant )
*
* This function schedules a data addition , but doesn ' t immidiately execute it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
*
* \ param [ in ] data the data to be added
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
* \ param [ in ] overwrite if false function throws LMDBAL : : Exist on repeated key , if true - overwrites it
* \ returns new actual amount of records in the storage
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Exist thrown if overwrite = = false and at least one of the keys of data already exists in the storage
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
uint32_t LMDBAL : : Storage < K , V > : : addRecords ( const std : : map < K , V > & data , TransactionID txn , bool overwrite ) {
ensureOpened ( addRecordsMethodName ) ;
2023-03-20 15:37:13 +00:00
MDB_val lmdbKey , lmdbData ;
int rc ;
for ( const std : : pair < const K , V > & pair : data ) {
2023-08-05 20:13:43 +00:00
lmdbKey = keySerializer . setData ( pair . first ) ;
lmdbData = valueSerializer . setData ( pair . second ) ;
2023-03-20 15:37:13 +00:00
rc = mdb_put ( txn , dbi , & lmdbKey , & lmdbData , overwrite ? 0 : MDB_NOOVERWRITE ) ;
2023-04-05 23:01:24 +00:00
if ( rc = = MDB_KEYEXIST )
throwDuplicate ( toString ( pair . first ) ) ;
2023-03-20 15:37:13 +00:00
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
}
MDB_stat stat ;
rc = mdb_stat ( txn , dbi , & stat ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwUnknown ( rc ) ;
2023-03-20 15:37:13 +00:00
2023-03-28 20:45:35 +00:00
return stat . ms_entries ;
2023-03-20 15:37:13 +00:00
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Removes one of the records
*
* Take a note that if the storage didn ' t have a record you want to remove LMDBAL : : NotFound is thrown
*
* \ param [ in ] key key of the record you wish to be removed
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : NotFound thrown if the record with given key wasn ' t found
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-03-21 11:05:54 +00:00
void LMDBAL : : Storage < K , V > : : removeRecord ( const K & key ) {
2023-03-20 15:37:13 +00:00
ensureOpened ( removeRecordMethodName ) ;
TransactionID txn = beginTransaction ( ) ;
2023-03-28 20:45:35 +00:00
try {
2023-04-04 23:27:31 +00:00
Storage < K , V > : : removeRecord ( key , txn ) ;
2023-03-28 20:45:35 +00:00
} catch ( . . . ) {
abortTransaction ( txn ) ;
throw ;
}
commitTransaction ( txn ) ;
}
2023-04-09 17:19:23 +00:00
/**
* \ brief Removes one of the records ( transaction variant )
*
* Take a note that if the storage didn ' t have a record you want to remove LMDBAL : : NotFound is thrown
* This function schedules a record removal , but doesn ' t immidiately execute it .
* You can obtain LMDBAL : : TransactionID calling LMDBAL : : Base : : beginTransaction ( ) .
*
2023-08-10 23:07:12 +00:00
* \ param [ in ] key key of the record you wish to be removed
2023-04-09 17:19:23 +00:00
* \ param [ in ] txn transaction ID , needs to be a writable transaction !
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : NotFound thrown if the record with given key wasn ' t found
* \ exception LMDBAL : : Unknown thrown if something unexpected happend within lmdb
*/
2023-03-28 20:45:35 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : removeRecord ( const K & key , TransactionID txn ) {
ensureOpened ( removeRecordMethodName ) ;
2023-08-05 20:13:43 +00:00
MDB_val lmdbKey = keySerializer . setData ( key ) ;
2023-03-20 15:37:13 +00:00
int rc = mdb_del ( txn , dbi , & lmdbKey , NULL ) ;
if ( rc ! = MDB_SUCCESS )
2023-03-28 20:45:35 +00:00
throwNotFoundOrUnknown ( rc , toString ( key ) ) ;
2023-03-20 15:37:13 +00:00
}
2023-04-09 17:19:23 +00:00
/**
2023-04-12 15:36:33 +00:00
* \ brief A private virtual function I need to open each storage in the database
2023-04-09 17:19:23 +00:00
*
2023-04-12 15:36:33 +00:00
* \ param [ in ] transaction - lmdb transaction to call < a class = " el " href= " http : //www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>
* \ returns MDB_SUCCESS if everything went smooth or MDB_ < error > - like error code
2023-04-09 17:19:23 +00:00
*/
2023-03-20 15:37:13 +00:00
template < class K , class V >
2023-08-05 20:13:43 +00:00
int LMDBAL : : Storage < K , V > : : open ( MDB_txn * transaction ) {
2023-08-15 18:48:19 +00:00
return makeStorage < K , V > ( transaction , duplicates ) ;
2023-03-20 15:37:13 +00:00
}
2023-08-05 20:13:43 +00:00
/**
* \ brief A private virtual function I need to close each storage in the database
*/
template < class K , class V >
void LMDBAL : : Storage < K , V > : : close ( ) {
for ( Cursor < K , V > * cursor : cursors )
cursor - > terminated ( ) ;
iStorage : : close ( ) ;
}
2023-08-10 23:07:12 +00:00
/**
* \ brief Creates cursor
*
* \ returns LMDBAL : : Cursor for this storage and returs you a pointer to a created cursor
*/
2023-08-05 20:13:43 +00:00
template < class K , class V >
LMDBAL : : Cursor < K , V > * LMDBAL : : Storage < K , V > : : createCursor ( ) {
Cursor < K , V > * cursor = new Cursor < K , V > ( this ) ;
cursors . insert ( cursor ) ;
return cursor ;
}
2023-08-15 18:48:19 +00:00
/**
* \ brief Reads current storage flags it was opened with
*
* This function exists mostly for testing purposes
*
* \ returns Third out parameter of < a href = " http://www.lmdb.tech/doc/group__internal.html#ga95ba4cb721035478a8705e57b91ae4d4 " > mdb_dbi_flags < / a > function
*
* \ exception LMDBAL : : Closed thrown if the database was not opened
* \ exception LMDBAL : : Unknown if the result of < a href = " http://www.lmdb.tech/doc/group__internal.html#ga95ba4cb721035478a8705e57b91ae4d4 " > mdb_dbi_flags < / a > was not successfull
*/
template < class K , class V >
uint32_t LMDBAL : : Storage < K , V > : : flags ( ) const {
ensureOpened ( flagsMethodName ) ;
uint32_t result ;
TransactionID txn = beginReadOnlyTransaction ( ) ;
int res = mdb_dbi_flags ( txn , dbi , & result ) ;
abortTransaction ( txn ) ;
if ( res ! = MDB_SUCCESS )
throwUnknown ( res ) ;
return result ;
}
2023-08-10 23:07:12 +00:00
/**
* \ brief Destroys cursor
*
* This a normal way to discard a cursor you don ' t need anymore
*
* \ param [ in ] cursor a pointer to a cursor you want to destroy
*
2023-08-15 18:48:19 +00:00
* \ exception LMDBAL : : Unknown thrown if you try to destroy something that this storage didn ' t create
2023-08-10 23:07:12 +00:00
*/
2023-08-05 20:13:43 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : destroyCursor ( Cursor < K , V > * cursor ) {
2023-08-10 23:07:12 +00:00
typename std : : set < Cursor < K , V > * > : : const_iterator itr = cursors . find ( cursor ) ;
if ( itr = = cursors . end ( ) )
throwUnknown ( " An attempt to destroy a cursor the storage doesn't own " ) ;
cursors . erase ( itr ) ;
2023-08-05 20:13:43 +00:00
delete cursor ;
}
2023-08-10 23:07:12 +00:00
/**
* \ brief A private virtual function that cursor calls when he reads a record , does nothing here but populates the LMDBAL : : Cache
*
* \ param [ in ] key a key of discovered record
* \ param [ in ] value a value of discovered record
*/
2023-08-09 17:41:15 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : discoveredRecord ( const K & key , const V & value ) const {
UNUSED ( key ) ;
UNUSED ( value ) ;
}
2023-08-10 23:07:12 +00:00
/**
* \ brief A private virtual function that cursor calls when he reads a record , does nothing here but populates the LMDBAL : : Cache
*
* \ param [ in ] key a key of discovered record
* \ param [ in ] value a value of discovered record
* \ param [ in ] txn TransactionID under which the dicovery happened , to avoid not commited changes collisions
*/
2023-08-09 17:41:15 +00:00
template < class K , class V >
void LMDBAL : : Storage < K , V > : : discoveredRecord ( const K & key , const V & value , TransactionID txn ) const {
UNUSED ( key ) ;
UNUSED ( value ) ;
UNUSED ( txn ) ;
}
2023-04-12 15:36:33 +00:00
/**
* \ brief A functiion to actually open < a class = " el " href= " http : //www.lmdb.tech/doc/group__mdb.html#gadbe68a06c448dfb62da16443d251a78b">MDB_dbi</a> storage
*
* \ tparam K type of keys in opening storage
*
* \ param [ in ] transaction - lmdb transaction to call < a class = " el " href= " http : //www.lmdb.tech/doc/group__mdb.html#gac08cad5b096925642ca359a6d6f0562a">mdb_dbi_open</a>, must be a writable transaction!
2023-08-15 18:48:19 +00:00
* \ param [ in ] duplicates - true if you wish to enable duplicates support for the storage
*
2023-04-12 15:36:33 +00:00
* \ returns MDB_SUCCESS if everything went smooth or MDB_ < error > - like error code
*
2023-08-15 18:48:19 +00:00
* This is a way to optimise database using MDB_INTEGERKEY flag ,
* when the key is actually kind of an integer
2023-04-12 15:36:33 +00:00
* This infrastructure also allowes us to customize mdb_dbi_open call in the future
*/
2023-08-15 18:48:19 +00:00
template < class K , class V >
inline int LMDBAL : : iStorage : : makeStorage ( MDB_txn * transaction , bool duplicates ) {
unsigned int flags = MDB_CREATE ;
if constexpr ( std : : is_integral < K > : : value )
flags | = MDB_INTEGERKEY ;
2023-03-20 15:37:13 +00:00
2023-08-15 18:48:19 +00:00
if ( duplicates ) {
flags | = MDB_DUPSORT ;
2023-03-20 15:37:13 +00:00
2023-08-15 18:48:19 +00:00
if constexpr ( std : : is_integral < V > : : value )
flags | = MDB_INTEGERDUP | MDB_DUPFIXED ;
}
2023-03-20 15:37:13 +00:00
2023-08-15 18:48:19 +00:00
return mdb_dbi_open ( transaction , name . c_str ( ) , flags , & dbi ) ;
2023-03-20 15:37:13 +00:00
}
2023-04-09 17:19:23 +00:00
/**
* \ brief A method to cast a value ( which can be a value or a key ) to string .
*
* This function is mainly used in exceptions , to report which key was duplicated or not found .
* You can define your own specializations to this function in case std : : to_string doesn ' t cover your case
*
* \ param [ in ] value a value that should be converted to string
* \ returns a string presentation of value
*/
2023-03-20 15:37:13 +00:00
template < class T >
2023-03-21 11:05:54 +00:00
inline std : : string LMDBAL : : iStorage : : toString ( const T & value ) {
2023-03-20 15:37:13 +00:00
return std : : to_string ( value ) ;
}
2023-04-12 15:36:33 +00:00
/**
* \ brief A method to cast a value ( which can be a value or a key ) to string .
*
* QString spectialization
*
* \ param [ in ] value a value that should be converted to string
* \ returns a string presentation of value
*/
2023-03-20 15:37:13 +00:00
template < >
2023-03-21 11:05:54 +00:00
inline std : : string LMDBAL : : iStorage : : toString ( const QString & value ) {
2023-03-20 15:37:13 +00:00
return value . toStdString ( ) ;
}
2023-04-12 15:36:33 +00:00
/**
* \ brief A method to cast a value ( which can be a value or a key ) to string .
*
* std : : string spectialization
*
* \ param [ in ] value a value that should be converted to string
* \ returns a string presentation of value
*/
2023-03-20 15:37:13 +00:00
template < >
2023-03-21 11:05:54 +00:00
inline std : : string LMDBAL : : iStorage : : toString ( const std : : string & value ) {
2023-03-20 15:37:13 +00:00
return value ;
}
2023-03-21 11:05:54 +00:00
# endif //LMDBAL_STORAGE_HPP