LMDBAL 0.6.2
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
58namespace LMDBAL {
59template<class K, class V>
60Storage<K, V>::~Storage() {
61 for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
62 pair.second->dropped();
63}
64}
65
80template<class K, class V>
81void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value) {
84 try {
85 Storage<K, V>::addRecord(key, value, txn);
86 } catch (...) {
88 throw;
89 }
90
92}
93
108template<class K, class V>
109void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, TransactionID txn) {
110 MDB_val lmdbKey = keySerializer.setData(key);
111 MDB_val lmdbData = valueSerializer.setData(value);
112
113 unsigned int flags = 0;
114 if (duplicates)
115 flags |= MDB_NODUPDATA;
116 else
117 flags |= MDB_NOOVERWRITE;
118
119 int rc = _mdbPut(txn, lmdbKey, lmdbData, flags);
120 if (rc != MDB_SUCCESS)
122}
123
143template<class K, class V>
144void LMDBAL::Storage<K, V>::addRecord(const K& key, const V& value, const WriteTransaction& txn) {
147}
148
165template<class K, class V>
166bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value) {
168
170 bool added;
171 try {
172 added = Storage<K, V>::forceRecord(key, value, txn);
173 } catch (...) {
174 abortTransaction(txn);
175 throw;
176 }
177
179 return added;
180}
181
199template<class K, class V>
200bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, TransactionID txn) {
201 bool added;
202 if (duplicates) {
203 try {
204 addRecord(key, value, txn);
205 added = true;
206 } catch (const LMDBAL::Exist& e) {
207 added = false;
208 }
209 } else {
210 MDB_val lmdbKey = keySerializer.setData(key);
211 MDB_val lmdbData;
212
213 int rc = _mdbGet(txn, lmdbKey, lmdbData);
214 switch (rc) {
215 case MDB_SUCCESS:
216 added = false;
217 break;
218 case MDB_NOTFOUND:
219 added = true;
220 break;
221 default:
222 added = false;
223 throwUnknown(rc);
224 }
225
226 lmdbData = valueSerializer.setData(value);
227 rc = _mdbPut(txn, lmdbKey, lmdbData);
228 if (rc != MDB_SUCCESS)
229 throwUnknown(rc);
230 }
231 return added;
232}
233
255template<class K, class V>
256bool LMDBAL::Storage<K, V>::forceRecord(const K& key, const V& value, const WriteTransaction& txn) {
259}
260
280template<class K, class V>
281void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value) {
283
285 try {
286 Storage<K, V>::changeRecord(key, value, txn);
287 } catch (...) {
288 abortTransaction(txn);
289 throw;
290 }
291
293}
294
315template<class K, class V>
316void LMDBAL::Storage<K, V>::changeRecord(const K& key, const V& value, TransactionID txn) {
317 MDB_cursor* cursor;
318 int rc = _mdbCursorOpen(txn, &cursor);
319 if (rc != MDB_SUCCESS)
320 throwUnknown(rc);
321
322 MDB_val lmdbKey = keySerializer.setData(key);
323 MDB_val lmdbData;
324 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_SET);
325 if (rc != MDB_SUCCESS)
327
328 MDB_val lmdbNewData = valueSerializer.setData(value);
329 bool sameSize = lmdbData.mv_size == lmdbNewData.mv_size;
330 int firstDifferentByte = 0;
331 if (sameSize) { //can compare only if they are the same size
332 firstDifferentByte = memcmp(lmdbData.mv_data, lmdbNewData.mv_data, lmdbData.mv_size);
333 if (firstDifferentByte == 0) { //old and new is the same, nothing to do
334 _mdbCursorClose(cursor);
335 return;
336 }
337 }
338
339 unsigned int flags = MDB_CURRENT;
340 if (duplicates && (!sameSize || firstDifferentByte < 0)) { //if new value is greater than the old one
341 rc = _mdbCursorDel(cursor); //we need to initiate duplicates sort, for it to be in the correct place
342 flags = MDB_NODUPDATA;
343 }
344
345 if (rc == MDB_SUCCESS)
346 rc = _mdbCursorPut(cursor, lmdbKey, lmdbNewData, flags);
347
348 _mdbCursorClose(cursor);
349 if (rc != MDB_SUCCESS)
351}
352
353
378template<class K, class V>
383
402template<class K, class V>
403V LMDBAL::Storage<K, V>::getRecord(const K& key) const {
405
406 V value;
407 Storage<K, V>::getRecord(key, value);
408 return value;
409}
410
429template<class K, class V>
430void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value) const {
432
434 try {
435 Storage<K, V>::getRecord(key, value, txn);
436 } catch (...) {
437 abortTransaction(txn);
438 throw;
439 }
440
441 abortTransaction(txn);
442}
443
462template<class K, class V>
464 V value;
465 Storage<K, V>::getRecord(key, value, txn);
466 return value;
467}
468
492template<class K, class V>
497
516template<class K, class V>
517void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, TransactionID txn) const {
518 MDB_val lmdbKey = keySerializer.setData(key);
519 MDB_val lmdbData;
520
521 int rc = _mdbGet(txn, lmdbKey, lmdbData);
522 if (rc != MDB_SUCCESS)
524
525 valueSerializer.deserialize(lmdbData, value);
526}
527
528
552template<class K, class V>
553void LMDBAL::Storage<K, V>::getRecord(const K& key, V& value, const Transaction& txn) const {
556}
557
567template<class K, class V>
568bool LMDBAL::Storage<K, V>::checkRecord(const K& key) const {
570
572 bool result;
573 try {
574 result = Storage<K, V>::checkRecord(key, txn);
575 } catch (...) {
576 abortTransaction(txn);
577 throw;
578 }
579
580 abortTransaction(txn);
581 return result;
582}
583
593template<class K, class V>
595 MDB_val lmdbKey = keySerializer.setData(key);
596 MDB_val lmdbData;
597
598 int rc = _mdbGet(txn, lmdbKey, lmdbData);
599 if (rc == MDB_SUCCESS)
600 return true;
601
602 if (rc != MDB_NOTFOUND)
603 throwUnknown(rc);
604
605 return false;
606}
607
622template<class K, class V>
627
639template<class K, class V>
640std::map<K, V> LMDBAL::Storage<K, V>::readAll() const {
642
643 std::map<K, V> result;
645 return result;
646}
647
661template<class K, class V>
662void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result) const {
664
666 try {
667 Storage<K, V>::readAll(result, txn);
668 } catch (...) {
669 abortTransaction(txn);
670 throw;
671 }
672
673 abortTransaction(txn);
674}
675
687template<class K, class V>
689 std::map<K, V> result;
690 Storage<K, V>::readAll(result, txn);
691 return result;
692}
693
710template<class K, class V>
715
728template<class K, class V>
729void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, TransactionID txn) const {
730 MDB_cursor* cursor;
731 MDB_val lmdbKey, lmdbData;
732
733 int rc = _mdbCursorOpen(txn, &cursor);
734 if (rc != MDB_SUCCESS)
735 throwUnknown(rc);
736
737 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_FIRST);
738 while (rc == MDB_SUCCESS) {
739 K key;
740 keySerializer.deserialize(lmdbKey, key);
741 std::pair<typename std::map<K, V>::iterator, bool> probe = result.emplace(key, V{});
742 if (probe.second) //I do this to avoid overwrites in case duplicates are enabled
743 valueSerializer.deserialize(lmdbData, probe.first->second);
744
745 rc = _mdbCursorGet(cursor, lmdbKey, lmdbData, MDB_NEXT);
746 }
747 _mdbCursorClose(cursor);
748 if (rc != MDB_NOTFOUND)
749 throwUnknown(rc);
750}
751
769template<class K, class V>
770void LMDBAL::Storage<K, V>::readAll(std::map<K, V>& result, const Transaction& txn) const {
773}
774
785template<class K, class V>
786void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data) {
788
790 try {
791 Storage<K, V>::replaceAll(data, txn);
792 } catch (...) {
793 abortTransaction(txn);
794 throw;
795 }
796
798}
799
810template<class K, class V>
811void LMDBAL::Storage<K, V>::replaceAll(const std::map<K, V>& data, TransactionID txn) {
812 int rc = drop(txn);
813 if (rc != MDB_SUCCESS)
814 throwUnknown(rc);
815
816 MDB_val lmdbKey, lmdbData;
817 for (const std::pair<const K, V>& pair : data) {
818 lmdbKey = keySerializer.setData(pair.first);
819 lmdbData = valueSerializer.setData(pair.second);
820
821 rc = _mdbPut(txn, lmdbKey, lmdbData, MDB_NOOVERWRITE); //TODO may be appending with cursor makes sence here?
822 if (rc != MDB_SUCCESS)
823 throwUnknown(rc);
824 }
825}
826
841template<class K, class V>
846
858template<class K, class V>
859uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, bool overwrite) {
861
863 uint32_t amount;
864 try {
865 amount = Storage<K, V>::addRecords(data, txn, overwrite);
866 } catch (...) {
867 abortTransaction(txn);
868 throw;
869 }
870
872 return amount;
873}
874
888template<class K, class V>
889LMDBAL::SizeType LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, TransactionID txn, bool overwrite) {
890 MDB_val lmdbKey, lmdbData;
891 int rc;
892 for (const std::pair<const K, V>& pair : data) {
893 lmdbKey = keySerializer.setData(pair.first);
894 lmdbData = valueSerializer.setData(pair.second);
895
896 rc = _mdbPut(txn, lmdbKey, lmdbData, overwrite ? 0 : MDB_NOOVERWRITE);
897 if (rc == MDB_KEYEXIST)
898 throwDuplicate(toString(pair.first));
899
900 if (rc != MDB_SUCCESS)
901 throwUnknown(rc);
902 }
903
904 MDB_stat stat;
905 rc = _mdbStat(txn, stat);
906 if (rc != MDB_SUCCESS)
907 throwUnknown(rc);
908
909 if (stat.ms_entries > std::numeric_limits<SizeType>::max())
910 throw std::runtime_error("LMDBAL::Storage::addRecords() - storage is full");
911
912 return static_cast<SizeType>(stat.ms_entries);
913}
914
931template<class K, class V>
932uint32_t LMDBAL::Storage<K, V>::addRecords(const std::map<K, V>& data, const WriteTransaction& txn, bool overwrite) {
934 return addRecords(data, extractTransactionId(txn, addRecordsMethodName), overwrite);
935}
936
948template<class K, class V>
951
953 try {
955 } catch (...) {
956 abortTransaction(txn);
957 throw;
958 }
959
961}
962
975template<class K, class V>
977 MDB_val lmdbKey = keySerializer.setData(key);
978 int rc = _mdbDel(txn, lmdbKey);
979 if (rc != MDB_SUCCESS)
981}
982
998template<class K, class V>
1003
1010template<class K, class V>
1011int LMDBAL::Storage<K, V>::open(MDB_txn* transaction) {
1012 return makeStorage<K, V>(transaction, duplicates);
1013}
1014
1018template<class K, class V>
1020 for (const std::pair<const uint32_t, Cursor<K, V>*>& pair : cursors)
1021 pair.second->terminated();
1022
1024}
1025
1034template<class K, class V>
1038
1051template<class K, class V>
1053 typename std::map<uint32_t, Cursor<K, V>*>::iterator itr = cursors.find(cursor.id);
1054 if (itr == cursors.end())
1055 throwUnknown("An attempt to destroy a cursor the storage doesn't own");
1056
1057 cursor.close();
1058 cursors.erase(itr);
1059 cursor.reset();
1060}
1061
1072template<class K, class V>
1075 uint32_t result;
1077
1078 int res = _mdbFlags(txn, result);
1079 abortTransaction(txn);
1080 if (res != MDB_SUCCESS)
1081 throwUnknown(res);
1082
1083 return result;
1084}
1085
1092template<class K, class V>
1093void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value) const {
1094 UNUSED(key);
1095 UNUSED(value);
1096}
1097
1105template<class K, class V>
1106void LMDBAL::Storage<K, V>::discoveredRecord(const K& key, const V& value, TransactionID txn) const {
1107 UNUSED(key);
1108 UNUSED(value);
1109 UNUSED(txn);
1110}
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:33
Thrown if there was a key conflict in one of the storages.
Definition exceptions.h:171
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:456
static constexpr std::string_view forceRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:120
static constexpr std::string_view removeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:122
TransactionID beginReadOnlyTransaction() const
Begins read-only transaction.
Definition storagecommon.cpp:428
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:446
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 constexpr std::string_view addRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:119
void throwDuplicateOrUnknown(int rc, std::string_view key) const
Throws LMDBAL::Exist or LMDBAL::Unknown.
Definition storagecommon.cpp:301
virtual void close()
A private virtual function to close each storage in the database.
Definition storagecommon.cpp:57
static constexpr std::string_view getRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:124
static constexpr std::string_view changeRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:121
const std::string name
this storage name
Definition storagecommon.h:112
TransactionID extractTransactionId(const Transaction &txn, std::string_view action={}) const
Checks if the transaction is still active, returns inner TransactionID.
Definition storagecommon.cpp:72
static constexpr std::string_view readAllMethodName
member function name, just for exceptions
Definition storagecommon.h:125
static constexpr std::string_view checkRecordMethodName
member function name, just for exceptions
Definition storagecommon.h:123
static constexpr std::string_view replaceAllMethodName
member function name, just for exceptions
Definition storagecommon.h:126
void ensureOpened(std::string_view methodName) const
Helper function; throws an exception if the database is not opened.
Definition storagecommon.cpp:135
void throwNotFoundOrUnknown(int rc, std::string_view key) const
Throws LMDBAL::NotFound or LMDBAL::Unknown (transaction variant).
Definition storagecommon.cpp:319
void throwUnknown(int rc, TransactionID txn) const
Throws LMDBAL::Unknown (transaction vairiant).
Definition storagecommon.cpp:336
TransactionID beginTransaction() const
Begins writable transaction.
Definition storagecommon.cpp:438
const bool duplicates
true if storage supports duplicates
Definition storagecommon.h:113
static constexpr std::string_view addRecordsMethodName
member function name, just for exceptions
Definition storagecommon.h:127
static constexpr std::string_view flagsMethodName
member function name, just for exceptions
Definition storagecommon.h:117
void throwDuplicate(std::string_view key) const
Throws LMDBAL::Exist.
Definition storagecommon.cpp:394
virtual bool checkRecord(const K &key, TransactionID txn) const
Chechs if storage has value (private transaction variant).
Definition storage.hpp:594
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:109
virtual std::map< K, V > readAll() const
Reads whole storage into a map.
Definition storage.hpp:640
virtual int drop(TransactionID transaction)
Drops content of a storage interface (transaction variant).
Definition storagecommon.cpp:109
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:1093
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:811
void destroyCursor(Cursor< K, V > &cursor)
Frees cursor.
Definition storage.hpp:1052
uint32_t flags() const
Reads current storage flags it was opened with.
Definition storage.hpp:1073
virtual void removeRecord(const K &key, TransactionID txn)
Removes one of the records (private transaction variant).
Definition storage.hpp:976
void close() override
A private virtual method I need to close each storage in the database.
Definition storage.hpp:1019
Cursor< K, V > createCursor()
Creates cursor.
Definition storage.hpp:1035
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:889
virtual std::map< K, V > readAll(TransactionID txn) const
Reads whole storage into a map (private transaction variant).
Definition storage.hpp:688
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:200
virtual void changeRecord(const K &key, const V &value, TransactionID txn)
Changes key-value record to the storage (private transaction variant).
Definition storage.hpp:316
virtual void getRecord(const K &key, V &value, TransactionID txn) const
Gets the record from the database (private transaction, reference variant).
Definition storage.hpp:517
Storage(Base *parent, const std::string &name, bool duplicates=false)
Creates a storage.
Definition storage.hpp:48
int open(MDB_txn *transaction) override
A private virtual method I need to open each storage in the database.
Definition storage.hpp:1011
Public read only transaction.
Definition transaction.h:27
Public writable transaction.
Definition transaction.h:54
Destroys a cache.
Definition base.h:36
MDB_txn * TransactionID
I'm going to use transaction pointers as transaction IDs.
Definition base.h:52
uint32_t SizeType
All LMDBAL sizes are uint32.
Definition base.h:53