LMDBAL 0.6.0
LMDB (Lightning Memory-Mapped Database Manager) Abstraction Layer
Loading...
Searching...
No Matches
storage.hpp
1/*
2 * LMDB Abstraction Layer.
3 * Copyright (C) 2023 Yury Gubich <blue@macaw.me>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "storage.h"
22#include "exceptions.h"
23
24#define UNUSED(x) (void)(x)
25
39
47template<class K, class V>
48LMDBAL::Storage<K, V>::Storage(Base* parent, const std::string& name, bool duplicates):
52 cursors()
53{}
54
58template<class K, class V>
60 for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
61 pair.second->dropped();
62}
63
78template<class K, class V>
79void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value) {
81 TransactionID txn = beginTransaction();
82 try {
83 Storage<K, V>::addRecord(key, value, txn);
84 } catch (...) {
86 throw;
87 }
88
90}
91
106template<class K, class V>
107void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
108 MDB_val lmdbKey = keySerializer.setData(key);
109 MDB_val lmdbData = valueSerializer.setData(value);
110
111 unsigned int flags = 0;
112 if (duplicates)
113 flags |= MDB_NODUPDATA;
114 else
115 flags |= MDB_NOOVERWRITE;
116
117 int rc = _mdbPut(txn, lmdbKey, lmdbData, flags);
118 if (rc != MDB_SUCCESS)
120}
121
141template<class K, class V>
142void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, const WriteTransaction& txn) {
145}
146
163template<class K, class V>
164bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value) {
166
167 TransactionID txn = beginTransaction();
168 bool added;
169 try {
170 added = Storage<K, V>::forceRecord(key, value, txn);
171 } catch (...) {
172 abortTransaction(txn);
173 throw;
174 }
175
177 return added;
178}
179
197template<class K, class V>
198bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
199 bool added;
200 if (duplicates) {
201 try {
202 addRecord(key, value, txn);
203 added = true;
204 } catch (const LMDBAL::Exist& e) {
205 added = false;
206 }
207 } else {
208 MDB_val lmdbKey = keySerializer.setData(key);
209 MDB_val lmdbData;
210
211 int rc = _mdbGet(txn, lmdbKey, lmdbData);
212 switch (rc) {
213 case MDB_SUCCESS:
214 added = false;
215 break;
216 case MDB_NOTFOUND:
217 added = true;
218 break;
219 default:
220 added = false;
221 throwUnknown(rc);
222 }
223
224 lmdbData = valueSerializer.setData(value);
225 rc = _mdbPut(txn, lmdbKey, lmdbData);
226 if (rc != MDB_SUCCESS)
227 throwUnknown(rc);
228 }
229 return added;
230}
231
253template<class K, class V>
254bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, const WriteTransaction& txn) {
257}
258
278template<class K, class V>
279void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value) {
281
282 TransactionID txn = beginTransaction();
283 try {
284 Storage<K, V>::changeRecord(key, value, txn);
285 } catch (...) {
286 abortTransaction(txn);
287 throw;
288 }
289
291}
292
313template<class K, class V>
314void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
315 MDB_cursor* cursor;
316 int rc = _mdbCursorOpen(txn, &cursor);
317 if (rc != MDB_SUCCESS)
318 throwUnknown(rc);
319
320 MDB_val lmdbKey = keySerializer.setData(key);
321 MDB_val lmdbData;
322 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_SET);
323 if (rc != MDB_SUCCESS)
325
326 MDB_val lmdbNewData = valueSerializer.setData(value);
327 bool sameSize = lmdbData.mv_size == lmdbNewData.mv_size;
328 int firstDifferentByte = 0;
329 if (sameSize) { //can compare only if they are the same size
330 firstDifferentByte = memcmp(lmdbData.mv_data, lmdbNewData.mv_data, lmdbData.mv_size);
331 if (firstDifferentByte == 0) { //old and new is the same, nothing to do
332 _mdbCursorClose(cursor);
333 return;
334 }
335 }
336
337 unsigned int flags = MDB_CURRENT;
338 if (duplicates && (!sameSize || firstDifferentByte < 0)) { //if new value is greater than the old one
339 rc = _mdbCursorDel(cursor); //we need to initiate duplicates sort, for it to be in the correct place
340 flags = MDB_NODUPDATA;
341 }
342
343 if (rc == MDB_SUCCESS)
344 rc = _mdbCursorPut(cursor, lmdbKey, lmdbNewData, flags);
345
346 _mdbCursorClose(cursor);
347 if (rc != MDB_SUCCESS)
349}
350
351
376template<class K, class V>
381
400template<class K, class V>
401V LMDBAL::Storage<K, V>::getRecord(const K& key) const {
403
404 V value;
405 Storage<K, V>::getRecord(key, value);
406 return value;
407}
408
427template<class K, class V>
428void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value) const {
430
431 TransactionID txn = beginReadOnlyTransaction();
432 try {
433 Storage<K, V>::getRecord(key, value, txn);
434 } catch (...) {
435 abortTransaction(txn);
436 throw;
437 }
438
439 abortTransaction(txn);
440}
441
460template<class K, class V>
461V LMDBAL::Storage<K, V>::getRecord(const K& key, TransactionID txn) const {
462 V value;
463 Storage<K, V>::getRecord(key, value, txn);
464 return value;
465}
466
490template<class K, class V>
495
514template<class K, class V>
515void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn) const {
516 MDB_val lmdbKey = keySerializer.setData(key);
517 MDB_val lmdbData;
518
519 int rc = _mdbGet(txn, lmdbKey, lmdbData);
520 if (rc != MDB_SUCCESS)
522
523 valueSerializer.deserialize(lmdbData, value);
524}
525
526
550template<class K, class V>
551void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, const Transaction& txn) const {
554}
555
565template<class K, class V>
566bool LMDBAL::Storage<K, V>::checkRecord(const K& key) const {
568
569 TransactionID txn = beginReadOnlyTransaction();
570 bool result;
571 try {
572 result = Storage<K, V>::checkRecord(key, txn);
573 } catch (...) {
574 abortTransaction(txn);
575 throw;
576 }
577
578 abortTransaction(txn);
579 return result;
580}
581
591template<class K, class V>
592bool LMDBAL::Storage<K, V>::checkRecord(const K& key, TransactionID txn) const {
593 MDB_val lmdbKey = keySerializer.setData(key);
594 MDB_val lmdbData;
595
596 int rc = _mdbGet(txn, lmdbKey, lmdbData);
597 if (rc == MDB_SUCCESS)
598 return true;
599
600 if (rc != MDB_NOTFOUND)
601 throwUnknown(rc);
602
603 return false;
604}
605
620template<class K, class V>
625
637template<class K, class V>
638std::map<K, V> LMDBAL::Storage<K, V>::readAll() const {
640
641 std::map<K, V> result;
643 return result;
644}
645
659template<class K, class V>
660void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result) const {
662
663 TransactionID txn = beginReadOnlyTransaction();
664 try {
665 Storage<K, V>::readAll(result, txn);
666 } catch (...) {
667 abortTransaction(txn);
668 throw;
669 }
670
671 abortTransaction(txn);
672}
673
685template<class K, class V>
686std::map<K, V> LMDBAL::Storage<K, V>::readAll(TransactionID txn) const {
687 std::map<K, V> result;
688 Storage<K, V>::readAll(result, txn);
689 return result;
690}
691
708template<class K, class V>
713
726template<class K, class V>
727void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) const {
728 MDB_cursor* cursor;
729 MDB_val lmdbKey, lmdbData;
730
731 int rc = _mdbCursorOpen(txn, &cursor);
732 if (rc != MDB_SUCCESS)
733 throwUnknown(rc);
734
735 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_FIRST);
736 while (rc == MDB_SUCCESS) {
737 K key;
738 keySerializer.deserialize(lmdbKey, key);
739 std::pair<typename std::map<K, V>::iterator, bool> probe = result.emplace(key, V{});
740 if (probe.second) //I do this to avoid overwrites in case duplicates are enabled
741 valueSerializer.deserialize(lmdbData, probe.first->second);
742
743 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_NEXT);
744 }
745 _mdbCursorClose(cursor);
746 if (rc != MDB_NOTFOUND)
747 throwUnknown(rc);
748}
749
767template<class K, class V>
768void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, const Transaction& txn) const {
771}
772
783template<class K, class V>
784void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data) {
786
787 TransactionID txn = beginTransaction();
788 try {
789 Storage<K, V>::replaceAll(data, txn);
790 } catch (...) {
791 abortTransaction(txn);
792 throw;
793 }
794
796}
797
808template<class K, class V>
809void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) {
810 int rc = drop(txn);
811 if (rc != MDB_SUCCESS)
812 throwUnknown(rc);
813
814 MDB_val lmdbKey, lmdbData;
815 for (const std::pair<const K, V>& pair : data) {
816 lmdbKey = keySerializer.setData(pair.first);
817 lmdbData = valueSerializer.setData(pair.second);
818
819 rc = _mdbPut(txn, lmdbKey, lmdbData, MDB_NOOVERWRITE); //TODO may be appending with cursor makes sence here?
820 if (rc != MDB_SUCCESS)
821 throwUnknown(rc);
822 }
823}
824
839template<class K, class V>
844
856template<class K, class V>
857uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, bool overwrite) {
859
860 TransactionID txn = beginTransaction();
861 uint32_t amount;
862 try {
863 amount = Storage<K, V>::addRecords(data, txn, overwrite);
864 } catch (...) {
865 abortTransaction(txn);
866 throw;
867 }
868
870 return amount;
871}
872
886template<class K, class V>
887uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) {
888 MDB_val lmdbKey, lmdbData;
889 int rc;
890 for (const std::pair<const K, V>& pair : data) {
891 lmdbKey = keySerializer.setData(pair.first);
892 lmdbData = valueSerializer.setData(pair.second);
893
894 rc = _mdbPut(txn, lmdbKey, lmdbData, overwrite ? 0 : MDB_NOOVERWRITE);
895 if (rc == MDB_KEYEXIST)
896 throwDuplicate(toString(pair.first));
897
898 if (rc != MDB_SUCCESS)
899 throwUnknown(rc);
900 }
901
902 MDB_stat stat;
903 rc = _mdbStat(txn, stat);
904 if (rc != MDB_SUCCESS)
905 throwUnknown(rc);
906
907 return stat.ms_entries;
908}
909
926template<class K, class V>
927uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, const WriteTransaction& txn, bool overwrite) {
929 return addRecords(data, extractTransactionId(txn, addRecordsMethodName), overwrite);
930}
931
943template<class K, class V>
946
947 TransactionID txn = beginTransaction();
948 try {
950 } catch (...) {
951 abortTransaction(txn);
952 throw;
953 }
954
956}
957
970template<class K, class V>
971void LMDBAL::Storage<K, V>::removeRecord(const K& key, TransactionID txn) {
972 MDB_val lmdbKey = keySerializer.setData(key);
973 int rc = _mdbDel(txn, lmdbKey);
974 if (rc != MDB_SUCCESS)
976}
977
993template<class K, class V>
998
1005template<class K, class V>
1006int LMDBAL::Storage<K, V>::open(MDB_txn* transaction) {
1007 return makeStorage<K, V>(transaction, duplicates);
1008}
1009
1013template<class K, class V>
1015 for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
1016 pair.second->terminated();
1017
1019}
1020
1029template<class K, class V>
1033
1046template<class K, class V>
1048 typename std::map<uint32_t, Cursor<K, V>*>::iterator itr = cursors.find(cursor.id);
1049 if (itr == cursors.end())
1050 throwUnknown("An attempt to destroy a cursor the storage doesn't own");
1051
1052 cursor.close();
1053 cursors.erase(itr);
1054 cursor.reset();
1055}
1056
1067template<class K, class V>
1070 uint32_t result;
1071 TransactionID txn = beginReadOnlyTransaction();
1072
1073 int res = _mdbFlags(txn, result);
1074 abortTransaction(txn);
1075 if (res != MDB_SUCCESS)
1076 throwUnknown(res);
1077
1078 return result;
1079}
1080
1087template<class K, class V>
1088void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value) const {
1089 UNUSED(key);
1090 UNUSED(value);
1091}
1092
1100template<class K, class V>
1101void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, TransactionID txn) const {
1102 UNUSED(key);
1103 UNUSED(value);
1104 UNUSED(txn);
1105}
void reset()
A private method that turns cursor into an empty one.
Definition cursorcommon.cpp:113
void close()
Termiates a sequence of operations with the cursor.
Definition cursorcommon.cpp:161
An object to iterate storages.
Definition cursor.h:32
Thrown if there was a key conflict in one of the storages.
Definition exceptions.h:178
StorageCommon(Base *parent, const std::string &name, bool duplicates=false)
Constructs a storage interface.
Definition storagecommon.cpp:42
void commitTransaction(TransactionID id)
Commits transaction.
Definition storagecommon.cpp:454
static const std::string getRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:123
TransactionID beginReadOnlyTransaction() const
Begins read-only transaction.
Definition storagecommon.cpp:426
int makeStorage(MDB_txn *transaction, bool duplicates=false)
A functiion to actually open MDB_dbi storage.
Definition storagecommon.hpp:38
void abortTransaction(TransactionID id) const
Aborts transaction.
Definition storagecommon.cpp:444
static std::string toString(const T &value)
A method to cast a value (which can be a value or a key) to string.
Definition storagecommon.hpp:71
static const std::string forceRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:119
virtual void close()
A private virtual function to close each storage in the database.
Definition storagecommon.cpp:57
static const std::string flagsMethodName
member function name, just for exceptions
Definition storagecommon.h:116
const std::string name
this storage name
Definition storagecommon.h:111
static const std::string checkRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:122
TransactionID extractTransactionId(const Transaction &txn, const std::string &action="") const
Checks if the transaction is still active, returns inner TransactionID.
Definition storagecommon.cpp:72
static const std::string readAllMethodName
member function name, just for exceptions
Definition storagecommon.h:124
static const std::string changeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:120
void throwDuplicate(const std::string &key) const
Throws LMDBAL::Exist.
Definition storagecommon.cpp:392
static const std::string addRecordsMethodName
member function name, just for exceptions
Definition storagecommon.h:126
void throwUnknown(int rc, TransactionID txn) const
Throws LMDBAL::Unknown (transaction vairiant)
Definition storagecommon.cpp:334
void throwDuplicateOrUnknown(int rc, const std::string &key) const
Throws LMDBAL::Exist or LMDBAL::Unknown.
Definition storagecommon.cpp:299
void throwNotFoundOrUnknown(int rc, const std::string &key) const
Throws LMDBAL::NotFound or LMDBAL::Unknown (transaction variant)
Definition storagecommon.cpp:317
TransactionID beginTransaction() const
Begins writable transaction.
Definition storagecommon.cpp:436
static const std::string addRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:118
const bool duplicates
true if storage supports duplicates
Definition storagecommon.h:112
void ensureOpened(const std::string &methodName) const
Helper function, thows exception if the database is not opened.
Definition storagecommon.cpp:135
static const std::string replaceAllMethodName
member function name, just for exceptions
Definition storagecommon.h:125
static const std::string removeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:121
virtual bool checkRecord(const K &key, TransactionID txn) const
Chechs if storage has value (private transaction variant)
Definition storage.hpp:592
Serializer< V > valueSerializer
internal object that would serialize and deserialize values
Definition storage.h:95
virtual void addRecord(const K &key, const V &value, TransactionID txn)
Adds a key-value record to the storage (private transaction variant)
Definition storage.hpp:107
virtual std::map< K, V > readAll() const
Reads whole storage into a map.
Definition storage.hpp:638
virtual int drop(TransactionID transaction)
Drops content of a storage interface (transaction variant)
Definition storagecommon.cpp:109
~Storage() override
Destroys a storage.
Definition storage.hpp:59
Serializer< K > keySerializer
internal object that would serialize and deserialize keys
Definition storage.h:94
std::map< uint32_t, Cursor< K, V > * > cursors
a set of cursors that has been created under this storage
Definition storage.h:96
virtual void discoveredRecord(const K &key, const V &value) const
A private virtual method that cursor calls when he reads a record, does nothing here but populates th...
Definition storage.hpp:1088
virtual void replaceAll(const std::map< K, V > &data, TransactionID txn)
Replaces the content of the whole storage with the given (private transaction variant)
Definition storage.hpp:809
void destroyCursor(Cursor< K, V > &cursor)
Frees cursor.
Definition storage.hpp:1047
uint32_t flags() const
Reads current storage flags it was opened with.
Definition storage.hpp:1068
virtual void removeRecord(const K &key, TransactionID txn)
Removes one of the records (private transaction variant)
Definition storage.hpp:971
void close() override
A private virtual method I need to close each storage in the database.
Definition storage.hpp:1014
Cursor< K, V > createCursor()
Creates cursor.
Definition storage.hpp:1030
virtual std::map< K, V > readAll(TransactionID txn) const
Reads whole storage into a map (private transaction variant)
Definition storage.hpp:686
virtual bool forceRecord(const K &key, const V &value, TransactionID txn)
Adds a key-value record to the storage, overwrites if it already exists (private transaction variant)
Definition storage.hpp:198
virtual void changeRecord(const K &key, const V &value, TransactionID txn)
Changes key-value record to the storage (private transaction variant)
Definition storage.hpp:314
virtual void getRecord(const K &key, V &value, TransactionID txn) const
Gets the record from the database (private transaction, reference variant)
Definition storage.hpp:515
Storage(Base *parent, const std::string &name, bool duplicates=false)
Creates a storage.
Definition storage.hpp:48
virtual uint32_t addRecords(const std::map< K, V > &data, TransactionID txn, bool overwrite=false)
Adds records in bulk (private transaction variant)
Definition storage.hpp:887
int open(MDB_txn *transaction) override
A private virtual method I need to open each storage in the database.
Definition storage.hpp:1006
Public read only transaction.
Definition transaction.h:27
Public writable transaction.
Definition transaction.h:54