summaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/changereplay.cpp7
-rw-r--r--common/domain/typeimplementations.cpp14
-rw-r--r--common/domain/typeimplementations_p.h10
-rw-r--r--common/index.cpp8
-rw-r--r--common/mail/threadindexer.cpp4
-rw-r--r--common/storage.h36
-rw-r--r--common/storage/entitystore.cpp106
-rw-r--r--common/storage/key.cpp5
-rw-r--r--common/storage/key.h1
-rw-r--r--common/storage_common.cpp83
-rw-r--r--common/storage_lmdb.cpp99
11 files changed, 261 insertions, 112 deletions
diff --git a/common/changereplay.cpp b/common/changereplay.cpp
index d7f46dc..96162b8 100644
--- a/common/changereplay.cpp
+++ b/common/changereplay.cpp
@@ -116,16 +116,15 @@ KAsync::Job<void> ChangeReplay::replayNextRevision()
116 } else { 116 } else {
117 // TODO: should not use internal representations 117 // TODO: should not use internal representations
118 const auto key = Storage::Key(Storage::Identifier::fromDisplayByteArray(uid), revision); 118 const auto key = Storage::Key(Storage::Identifier::fromDisplayByteArray(uid), revision);
119 const auto internalKey = key.toInternalByteArray();
120 const auto displayKey = key.toDisplayByteArray(); 119 const auto displayKey = key.toDisplayByteArray();
121 QByteArray entityBuffer; 120 QByteArray entityBuffer;
122 DataStore::mainDatabase(mMainStoreTransaction, type) 121 DataStore::mainDatabase(mMainStoreTransaction, type)
123 .scan(internalKey, 122 .scan(revision,
124 [&entityBuffer](const QByteArray &key, const QByteArray &value) -> bool { 123 [&entityBuffer](const size_t, const QByteArray &value) -> bool {
125 entityBuffer = value; 124 entityBuffer = value;
126 return false; 125 return false;
127 }, 126 },
128 [this, key](const DataStore::Error &) { SinkErrorCtx(mLogCtx) << "Failed to read the entity buffer " << key; }); 127 [this, key](const DataStore::Error &e) { SinkErrorCtx(mLogCtx) << "Failed to read the entity buffer " << key << "error:" << e; });
129 128
130 if (entityBuffer.isEmpty()) { 129 if (entityBuffer.isEmpty()) {
131 SinkErrorCtx(mLogCtx) << "Failed to replay change " << key; 130 SinkErrorCtx(mLogCtx) << "Failed to replay change " << key;
diff --git a/common/domain/typeimplementations.cpp b/common/domain/typeimplementations.cpp
index aedf889..f969072 100644
--- a/common/domain/typeimplementations.cpp
+++ b/common/domain/typeimplementations.cpp
@@ -89,7 +89,7 @@ void TypeImplementation<Mail>::configure(TypeIndex &index)
89 89
90QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() 90QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases()
91{ 91{
92 return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", 0}}, MailIndexConfig::databases()); 92 return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", Storage::IntegerKeys}}, MailIndexConfig::databases());
93} 93}
94 94
95void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) 95void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper)
@@ -132,7 +132,7 @@ void TypeImplementation<Folder>::configure(TypeIndex &index)
132 132
133QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() 133QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases()
134{ 134{
135 return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", 0}}, FolderIndexConfig::databases()); 135 return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", Storage::IntegerKeys}}, FolderIndexConfig::databases());
136} 136}
137 137
138void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) 138void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper)
@@ -157,7 +157,7 @@ void TypeImplementation<Contact>::configure(TypeIndex &index)
157 157
158QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() 158QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases()
159{ 159{
160 return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", 0}}, ContactIndexConfig::databases()); 160 return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", Storage::IntegerKeys}}, ContactIndexConfig::databases());
161} 161}
162 162
163void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) 163void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper)
@@ -185,7 +185,7 @@ void TypeImplementation<Addressbook>::configure(TypeIndex &index)
185 185
186QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() 186QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases()
187{ 187{
188 return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", 0}}, AddressbookIndexConfig::databases()); 188 return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", Storage::IntegerKeys}}, AddressbookIndexConfig::databases());
189} 189}
190 190
191void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) 191void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper)
@@ -207,7 +207,7 @@ void TypeImplementation<Event>::configure(TypeIndex &index)
207 207
208QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() 208QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases()
209{ 209{
210 return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", 0}}, EventIndexConfig::databases()); 210 return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", Storage::IntegerKeys}}, EventIndexConfig::databases());
211} 211}
212 212
213void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) 213void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper)
@@ -235,7 +235,7 @@ void TypeImplementation<Todo>::configure(TypeIndex &index)
235 235
236QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases() 236QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases()
237{ 237{
238 return merge(QMap<QByteArray, int>{{QByteArray{Todo::name} + ".main", 0}}, TodoIndexConfig::databases()); 238 return merge(QMap<QByteArray, int>{{QByteArray{Todo::name} + ".main", Storage::IntegerKeys}}, TodoIndexConfig::databases());
239} 239}
240 240
241void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper) 241void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper)
@@ -266,7 +266,7 @@ void TypeImplementation<Calendar>::configure(TypeIndex &index)
266 266
267QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases() 267QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases()
268{ 268{
269 return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", 0}}, CalendarIndexConfig::databases()); 269 return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", Storage::IntegerKeys}}, CalendarIndexConfig::databases());
270} 270}
271 271
272void TypeImplementation<Calendar>::configure(PropertyMapper &propertyMapper) 272void TypeImplementation<Calendar>::configure(PropertyMapper &propertyMapper)
diff --git a/common/domain/typeimplementations_p.h b/common/domain/typeimplementations_p.h
index 51af113..bfdea77 100644
--- a/common/domain/typeimplementations_p.h
+++ b/common/domain/typeimplementations_p.h
@@ -57,7 +57,7 @@ public:
57 template <typename EntityType> 57 template <typename EntityType>
58 static QMap<QByteArray, int> databases() 58 static QMap<QByteArray, int> databases()
59 { 59 {
60 return {{QByteArray{EntityType::name} +".index." + Property::name, 1}}; 60 return {{QByteArray{EntityType::name} +".index." + Property::name, Sink::Storage::AllowDuplicates}};
61 } 61 }
62}; 62};
63 63
@@ -74,7 +74,7 @@ public:
74 template <typename EntityType> 74 template <typename EntityType>
75 static QMap<QByteArray, int> databases() 75 static QMap<QByteArray, int> databases()
76 { 76 {
77 return {{QByteArray{EntityType::name} +".index." + Property::name + ".sort." + SortProperty::name, 1}}; 77 return {{QByteArray{EntityType::name} +".index." + Property::name + ".sort." + SortProperty::name, Sink::Storage::AllowDuplicates}};
78 } 78 }
79}; 79};
80 80
@@ -90,7 +90,7 @@ public:
90 template <typename EntityType> 90 template <typename EntityType>
91 static QMap<QByteArray, int> databases() 91 static QMap<QByteArray, int> databases()
92 { 92 {
93 return {{QByteArray{EntityType::name} +".index." + SortProperty::name + ".sorted", 1}}; 93 return {{QByteArray{EntityType::name} +".index." + SortProperty::name + ".sorted", Sink::Storage::AllowDuplicates}};
94 } 94 }
95}; 95};
96 96
@@ -106,7 +106,7 @@ public:
106 template <typename EntityType> 106 template <typename EntityType>
107 static QMap<QByteArray, int> databases() 107 static QMap<QByteArray, int> databases()
108 { 108 {
109 return {{QByteArray{EntityType::name} +".index." + Property::name + SecondaryProperty::name, 1}}; 109 return {{QByteArray{EntityType::name} +".index." + Property::name + SecondaryProperty::name, Sink::Storage::AllowDuplicates}};
110 } 110 }
111}; 111};
112 112
@@ -142,7 +142,7 @@ public:
142 template <typename EntityType> 142 template <typename EntityType>
143 static QMap<QByteArray, int> databases() 143 static QMap<QByteArray, int> databases()
144 { 144 {
145 return {{QByteArray{EntityType::name} +".index." + RangeBeginProperty::name + ".range." + RangeEndProperty::name, 1}}; 145 return {{QByteArray{EntityType::name} +".index." + RangeBeginProperty::name + ".range." + RangeEndProperty::name, Sink::Storage::AllowDuplicates}};
146 } 146 }
147}; 147};
148 148
diff --git a/common/index.cpp b/common/index.cpp
index 238a745..bf8fcfc 100644
--- a/common/index.cpp
+++ b/common/index.cpp
@@ -6,7 +6,7 @@ using Sink::Storage::Identifier;
6 6
7Index::Index(const QString &storageRoot, const QString &dbName, const QString &indexName, Sink::Storage::DataStore::AccessMode mode) 7Index::Index(const QString &storageRoot, const QString &dbName, const QString &indexName, Sink::Storage::DataStore::AccessMode mode)
8 : mTransaction(Sink::Storage::DataStore(storageRoot, dbName, mode).createTransaction(mode)), 8 : mTransaction(Sink::Storage::DataStore(storageRoot, dbName, mode).createTransaction(mode)),
9 mDb(mTransaction.openDatabase(indexName.toLatin1(), std::function<void(const Sink::Storage::DataStore::Error &)>(), true)), 9 mDb(mTransaction.openDatabase(indexName.toLatin1(), std::function<void(const Sink::Storage::DataStore::Error &)>(), Sink::Storage::AllowDuplicates)),
10 mName(indexName), 10 mName(indexName),
11 mLogCtx("index." + indexName.toLatin1()) 11 mLogCtx("index." + indexName.toLatin1())
12{ 12{
@@ -14,7 +14,7 @@ Index::Index(const QString &storageRoot, const QString &dbName, const QString &i
14 14
15Index::Index(const QString &storageRoot, const QString &name, Sink::Storage::DataStore::AccessMode mode) 15Index::Index(const QString &storageRoot, const QString &name, Sink::Storage::DataStore::AccessMode mode)
16 : mTransaction(Sink::Storage::DataStore(storageRoot, name, mode).createTransaction(mode)), 16 : mTransaction(Sink::Storage::DataStore(storageRoot, name, mode).createTransaction(mode)),
17 mDb(mTransaction.openDatabase(name.toLatin1(), std::function<void(const Sink::Storage::DataStore::Error &)>(), true)), 17 mDb(mTransaction.openDatabase(name.toLatin1(), std::function<void(const Sink::Storage::DataStore::Error &)>(), Sink::Storage::AllowDuplicates)),
18 mName(name), 18 mName(name),
19 mLogCtx("index." + name.toLatin1()) 19 mLogCtx("index." + name.toLatin1())
20{ 20{
@@ -22,14 +22,14 @@ Index::Index(const QString &storageRoot, const QString &name, Sink::Storage::Dat
22 22
23Index::Index(const QString &storageRoot, const Sink::Storage::DbLayout &layout, Sink::Storage::DataStore::AccessMode mode) 23Index::Index(const QString &storageRoot, const Sink::Storage::DbLayout &layout, Sink::Storage::DataStore::AccessMode mode)
24 : mTransaction(Sink::Storage::DataStore(storageRoot, layout, mode).createTransaction(mode)), 24 : mTransaction(Sink::Storage::DataStore(storageRoot, layout, mode).createTransaction(mode)),
25 mDb(mTransaction.openDatabase(layout.name, std::function<void(const Sink::Storage::DataStore::Error &)>(), true)), 25 mDb(mTransaction.openDatabase(layout.name, std::function<void(const Sink::Storage::DataStore::Error &)>(), Sink::Storage::AllowDuplicates)),
26 mName(layout.name), 26 mName(layout.name),
27 mLogCtx("index." + layout.name) 27 mLogCtx("index." + layout.name)
28{ 28{
29} 29}
30 30
31Index::Index(const QByteArray &name, Sink::Storage::DataStore::Transaction &transaction) 31Index::Index(const QByteArray &name, Sink::Storage::DataStore::Transaction &transaction)
32 : mDb(transaction.openDatabase(name, std::function<void(const Sink::Storage::DataStore::Error &)>(), true)), mName(name), 32 : mDb(transaction.openDatabase(name, std::function<void(const Sink::Storage::DataStore::Error &)>(), Sink::Storage::AllowDuplicates)), mName(name),
33 mLogCtx("index." + name) 33 mLogCtx("index." + name)
34{ 34{
35} 35}
diff --git a/common/mail/threadindexer.cpp b/common/mail/threadindexer.cpp
index c1d1aa8..b9de266 100644
--- a/common/mail/threadindexer.cpp
+++ b/common/mail/threadindexer.cpp
@@ -118,7 +118,7 @@ void ThreadIndexer::remove(const ApplicationDomain::ApplicationDomainType &entit
118 118
119QMap<QByteArray, int> ThreadIndexer::databases() 119QMap<QByteArray, int> ThreadIndexer::databases()
120{ 120{
121 return {{"mail.index.messageIdthreadId", 1}, 121 return {{"mail.index.messageIdthreadId", Sink::Storage::AllowDuplicates},
122 {"mail.index.threadIdmessageId", 1}}; 122 {"mail.index.threadIdmessageId", Sink::Storage::AllowDuplicates}};
123} 123}
124 124
diff --git a/common/storage.h b/common/storage.h
index 8904148..53fcf41 100644
--- a/common/storage.h
+++ b/common/storage.h
@@ -32,6 +32,11 @@
32namespace Sink { 32namespace Sink {
33namespace Storage { 33namespace Storage {
34 34
35extern int AllowDuplicates;
36extern int IntegerKeys;
37// Only useful with AllowDuplicates
38extern int IntegerValues;
39
35struct SINK_EXPORT DbLayout { 40struct SINK_EXPORT DbLayout {
36 typedef QMap<QByteArray, int> Databases; 41 typedef QMap<QByteArray, int> Databases;
37 DbLayout(); 42 DbLayout();
@@ -80,15 +85,24 @@ public:
80 */ 85 */
81 bool write(const QByteArray &key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); 86 bool write(const QByteArray &key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
82 87
88 // TODO: change resultHandlers and errorHandlers to take size_t instead
89 // of QByteArray for keys
90 bool write(const size_t key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
91
83 /** 92 /**
84 * Remove a key 93 * Remove a key
85 */ 94 */
86 void remove(const QByteArray &key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); 95 void remove(const QByteArray &key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
96
97 void remove(const size_t key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
98
87 /** 99 /**
88 * Remove a key-value pair 100 * Remove a key-value pair
89 */ 101 */
90 void remove(const QByteArray &key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); 102 void remove(const QByteArray &key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
91 103
104 void remove(const size_t key, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>());
105
92 /** 106 /**
93 * Read values with a given key. 107 * Read values with a given key.
94 * 108 *
@@ -101,6 +115,9 @@ public:
101 int scan(const QByteArray &key, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, 115 int scan(const QByteArray &key, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
102 const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>(), bool findSubstringKeys = false, bool skipInternalKeys = true) const; 116 const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>(), bool findSubstringKeys = false, bool skipInternalKeys = true) const;
103 117
118 int scan(const size_t key, const std::function<bool(size_t key, const QByteArray &value)> &resultHandler,
119 const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>(), bool skipInternalKeys = true) const;
120
104 /** 121 /**
105 * Finds the last value in a series matched by prefix. 122 * Finds the last value in a series matched by prefix.
106 * 123 *
@@ -119,6 +136,10 @@ public:
119 const std::function<void(const DataStore::Error &error)> &errorHandler = 136 const std::function<void(const DataStore::Error &error)> &errorHandler =
120 std::function<void(const DataStore::Error &error)>()) const; 137 std::function<void(const DataStore::Error &error)>()) const;
121 138
139 int findAllInRange(const size_t lowerBound, const size_t upperBound,
140 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
141 const std::function<void(const DataStore::Error &error)> &errorHandler = {}) const;
142
122 /** 143 /**
123 * Returns true if the database contains the substring key. 144 * Returns true if the database contains the substring key.
124 */ 145 */
@@ -163,8 +184,9 @@ public:
163 184
164 QList<QByteArray> getDatabaseNames() const; 185 QList<QByteArray> getDatabaseNames() const;
165 186
166 NamedDatabase openDatabase(const QByteArray &name = {"default"}, 187 NamedDatabase openDatabase(const QByteArray &name = { "default" },
167 const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const; 188 const std::function<void(const DataStore::Error &error)> &errorHandler = {},
189 int flags = 0) const;
168 190
169 Transaction(Transaction &&other); 191 Transaction(Transaction &&other);
170 Transaction &operator=(Transaction &&other); 192 Transaction &operator=(Transaction &&other);
@@ -224,10 +246,12 @@ public:
224 static qint64 cleanedUpRevision(const Transaction &); 246 static qint64 cleanedUpRevision(const Transaction &);
225 static void setCleanedUpRevision(Transaction &, qint64 revision); 247 static void setCleanedUpRevision(Transaction &, qint64 revision);
226 248
227 static QByteArray getUidFromRevision(const Transaction &, qint64 revision); 249 static QByteArray getUidFromRevision(const Transaction &, size_t revision);
228 static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); 250 static size_t getLatestRevisionFromUid(Transaction &, const QByteArray &uid);
229 static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); 251 static QList<size_t> getRevisionsUntilFromUid(DataStore::Transaction &, const QByteArray &uid, size_t lastRevision);
230 static void removeRevision(Transaction &, qint64 revision); 252 static QByteArray getTypeFromRevision(const Transaction &, size_t revision);
253 static void recordRevision(Transaction &, size_t revision, const QByteArray &uid, const QByteArray &type);
254 static void removeRevision(Transaction &, size_t revision);
231 static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); 255 static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type);
232 static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); 256 static void removeUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type);
233 static void getUids(const QByteArray &type, const Transaction &, const std::function<void(const QByteArray &uid)> &); 257 static void getUids(const QByteArray &type, const Transaction &, const std::function<void(const QByteArray &uid)> &);
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp
index 276ee6a..0640f1c 100644
--- a/common/storage/entitystore.cpp
+++ b/common/storage/entitystore.cpp
@@ -38,8 +38,9 @@ using namespace Sink::Storage;
38 38
39static QMap<QByteArray, int> baseDbs() 39static QMap<QByteArray, int> baseDbs()
40{ 40{
41 return {{"revisionType", 0}, 41 return {{"revisionType", Storage::IntegerKeys},
42 {"revisions", 0}, 42 {"revisions", Storage::IntegerKeys},
43 {"uidsToRevisions", Storage::AllowDuplicates | Storage::IntegerValues},
43 {"uids", 0}, 44 {"uids", 0},
44 {"default", 0}, 45 {"default", 0},
45 {"__flagtable", 0}}; 46 {"__flagtable", 0}};
@@ -242,12 +243,13 @@ bool EntityStore::add(const QByteArray &type, ApplicationDomainType entity, bool
242 const auto key = Key(identifier, newRevision); 243 const auto key = Key(identifier, newRevision);
243 244
244 DataStore::mainDatabase(d->transaction, type) 245 DataStore::mainDatabase(d->transaction, type)
245 .write(key.toInternalByteArray(), BufferUtils::extractBuffer(fbb), 246 .write(newRevision, BufferUtils::extractBuffer(fbb),
246 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; }); 247 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << entity.identifier() << newRevision; });
248
247 DataStore::setMaxRevision(d->transaction, newRevision); 249 DataStore::setMaxRevision(d->transaction, newRevision);
248 DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type); 250 DataStore::recordRevision(d->transaction, newRevision, entity.identifier(), type);
249 DataStore::recordUid(d->transaction, entity.identifier(), type); 251 DataStore::recordUid(d->transaction, entity.identifier(), type);
250 SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision; 252 SinkTraceCtx(d->logCtx) << "Wrote entity: " << entity.identifier() << type << newRevision << "key:" << key.toInternalByteArray();
251 return true; 253 return true;
252} 254}
253 255
@@ -319,8 +321,9 @@ bool EntityStore::modify(const QByteArray &type, const ApplicationDomainType &cu
319 const auto key = Key(identifier, newRevision); 321 const auto key = Key(identifier, newRevision);
320 322
321 DataStore::mainDatabase(d->transaction, type) 323 DataStore::mainDatabase(d->transaction, type)
322 .write(key.toInternalByteArray(), BufferUtils::extractBuffer(fbb), 324 .write(newRevision, BufferUtils::extractBuffer(fbb),
323 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << newEntity.identifier() << newRevision; }); 325 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << newEntity.identifier() << newRevision; });
326
324 DataStore::setMaxRevision(d->transaction, newRevision); 327 DataStore::setMaxRevision(d->transaction, newRevision);
325 DataStore::recordRevision(d->transaction, newRevision, newEntity.identifier(), type); 328 DataStore::recordRevision(d->transaction, newRevision, newEntity.identifier(), type);
326 SinkTraceCtx(d->logCtx) << "Wrote modified entity: " << newEntity.identifier() << type << newRevision; 329 SinkTraceCtx(d->logCtx) << "Wrote modified entity: " << newEntity.identifier() << type << newRevision;
@@ -356,8 +359,9 @@ bool EntityStore::remove(const QByteArray &type, const ApplicationDomainType &cu
356 const auto key = Key(identifier, newRevision); 359 const auto key = Key(identifier, newRevision);
357 360
358 DataStore::mainDatabase(d->transaction, type) 361 DataStore::mainDatabase(d->transaction, type)
359 .write(key.toInternalByteArray(), BufferUtils::extractBuffer(fbb), 362 .write(newRevision, BufferUtils::extractBuffer(fbb),
360 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; }); 363 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to write entity" << uid << newRevision; });
364
361 DataStore::setMaxRevision(d->transaction, newRevision); 365 DataStore::setMaxRevision(d->transaction, newRevision);
362 DataStore::recordRevision(d->transaction, newRevision, uid, type); 366 DataStore::recordRevision(d->transaction, newRevision, uid, type);
363 DataStore::removeUid(d->transaction, uid, type); 367 DataStore::removeUid(d->transaction, uid, type);
@@ -375,30 +379,33 @@ void EntityStore::cleanupEntityRevisionsUntil(qint64 revision)
375 } 379 }
376 SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType; 380 SinkTraceCtx(d->logCtx) << "Cleaning up revision " << revision << uid << bufferType;
377 const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray(); 381 const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray();
378 DataStore::mainDatabase(d->transaction, bufferType)
379 .scan(internalUid,
380 [&](const QByteArray &key, const QByteArray &data) -> bool {
381 EntityBuffer buffer(const_cast<const char *>(data.data()), data.size());
382 if (!buffer.isValid()) {
383 SinkWarningCtx(d->logCtx) << "Read invalid buffer from disk";
384 } else {
385 const auto metadata = flatbuffers::GetRoot<Metadata>(buffer.metadataBuffer());
386 const qint64 rev = metadata->revision();
387 const auto isRemoval = metadata->operation() == Operation_Removal;
388 // Remove old revisions, and the current if the entity has already been removed
389 if (rev < revision || isRemoval) {
390 DataStore::removeRevision(d->transaction, rev);
391 DataStore::mainDatabase(d->transaction, bufferType).remove(key);
392 }
393 //Don't cleanup more than specified
394 if (rev >= revision) {
395 return false;
396 }
397 }
398 382
399 return true; 383 // Remove old revisions
400 }, 384 const auto revisionsToRemove = DataStore::getRevisionsUntilFromUid(d->transaction, uid, revision);
401 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error while reading: " << error.message; }, true); 385
386 for (const auto &revisionToRemove : revisionsToRemove) {
387 DataStore::removeRevision(d->transaction, revisionToRemove);
388 DataStore::mainDatabase(d->transaction, bufferType).remove(revisionToRemove);
389 }
390
391 // And remove the specified revision only if marked for removal
392 DataStore::mainDatabase(d->transaction, bufferType).scan(revision, [&](size_t, const QByteArray &data) {
393 EntityBuffer buffer(const_cast<const char *>(data.data()), data.size());
394 if (!buffer.isValid()) {
395 SinkWarningCtx(d->logCtx) << "Read invalid buffer from disk";
396 return false;
397 }
398
399 const auto metadata = flatbuffers::GetRoot<Metadata>(buffer.metadataBuffer());
400 const qint64 rev = metadata->revision();
401 if (metadata->operation() == Operation_Removal) {
402 DataStore::removeRevision(d->transaction, revision);
403 DataStore::mainDatabase(d->transaction, bufferType).remove(revision);
404 }
405
406 return false;
407 });
408
402 DataStore::setCleanedUpRevision(d->transaction, revision); 409 DataStore::setCleanedUpRevision(d->transaction, revision);
403} 410}
404 411
@@ -437,13 +444,23 @@ QVector<Identifier> EntityStore::fullScan(const QByteArray &type)
437 QSet<Identifier> keys; 444 QSet<Identifier> keys;
438 DataStore::mainDatabase(d->getTransaction(), type) 445 DataStore::mainDatabase(d->getTransaction(), type)
439 .scan(QByteArray(), 446 .scan(QByteArray(),
440 [&](const QByteArray &key, const QByteArray &value) -> bool { 447 [&](const QByteArray &key, const QByteArray &data) -> bool {
441 const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier(); 448 EntityBuffer buffer(const_cast<const char *>(data.data()), data.size());
442 if (keys.contains(uid)) { 449 if (!buffer.isValid()) {
450 SinkWarningCtx(d->logCtx) << "Read invalid buffer from disk";
451 return true;
452 }
453
454 size_t revision = *reinterpret_cast<const size_t*>(key.constData());
455
456 const auto metadata = flatbuffers::GetRoot<Metadata>(buffer.metadataBuffer());
457 const QByteArray uid = DataStore::getUidFromRevision(d->getTransaction(), revision);
458 const auto identifier = Sink::Storage::Identifier::fromDisplayByteArray(uid);
459 if (keys.contains(identifier)) {
443 //Not something that should persist if the replay works, so we keep a message for now. 460 //Not something that should persist if the replay works, so we keep a message for now.
444 SinkTraceCtx(d->logCtx) << "Multiple revisions for uid: " << Sink::Storage::Key::fromInternalByteArray(key) << ". This is normal if changereplay has not completed yet."; 461 SinkTraceCtx(d->logCtx) << "Multiple revisions for uid: " << Sink::Storage::Key::fromInternalByteArray(key) << ". This is normal if changereplay has not completed yet.";
445 } 462 }
446 keys << uid; 463 keys << identifier;
447 return true; 464 return true;
448 }, 465 },
449 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during fullScan query: " << error.message; }); 466 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during fullScan query: " << error.message; });
@@ -492,12 +509,12 @@ void EntityStore::indexLookup(const QByteArray &type, const QByteArray &property
492void EntityStore::readLatest(const QByteArray &type, const Identifier &id, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback) 509void EntityStore::readLatest(const QByteArray &type, const Identifier &id, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback)
493{ 510{
494 Q_ASSERT(d); 511 Q_ASSERT(d);
495 const auto internalKey = id.toInternalByteArray(); 512 const size_t revision = DataStore::getLatestRevisionFromUid(d->getTransaction(), id.toDisplayByteArray());
496 auto db = DataStore::mainDatabase(d->getTransaction(), type); 513 auto db = DataStore::mainDatabase(d->getTransaction(), type);
497 db.findLatest(internalKey, 514 db.scan(revision,
498 [=](const QByteArray &key, const QByteArray &value) { 515 [=](size_t, const QByteArray &value) {
499 const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier().toDisplayByteArray(); 516 callback(id.toDisplayByteArray(), Sink::EntityBuffer(value.data(), value.size()));
500 callback(uid, Sink::EntityBuffer(value.data(), value.size())); 517 return false;
501 }, 518 },
502 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during readLatest query: " << error.message << id; }); 519 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during readLatest query: " << error.message << id; });
503} 520}
@@ -546,9 +563,9 @@ void EntityStore::readEntity(const QByteArray &type, const QByteArray &displayKe
546{ 563{
547 const auto key = Key::fromDisplayByteArray(displayKey); 564 const auto key = Key::fromDisplayByteArray(displayKey);
548 auto db = DataStore::mainDatabase(d->getTransaction(), type); 565 auto db = DataStore::mainDatabase(d->getTransaction(), type);
549 db.scan(key.toInternalByteArray(), 566 db.scan(key.revision().toSizeT(),
550 [=](const QByteArray &key, const QByteArray &value) -> bool { 567 [=](size_t rev, const QByteArray &value) -> bool {
551 const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier().toDisplayByteArray(); 568 const auto uid = DataStore::getUidFromRevision(d->transaction, rev);
552 callback(uid, Sink::EntityBuffer(value.data(), value.size())); 569 callback(uid, Sink::EntityBuffer(value.data(), value.size()));
553 return false; 570 return false;
554 }, 571 },
@@ -652,10 +669,10 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid)
652{ 669{
653 bool found = false; 670 bool found = false;
654 bool alreadyRemoved = false; 671 bool alreadyRemoved = false;
655 const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray(); 672 const size_t revision = DataStore::getLatestRevisionFromUid(d->getTransaction(), uid);
656 DataStore::mainDatabase(d->transaction, type) 673 DataStore::mainDatabase(d->transaction, type)
657 .findLatest(internalUid, 674 .scan(revision,
658 [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) { 675 [&found, &alreadyRemoved](size_t, const QByteArray &data) {
659 auto entity = GetEntity(data.data()); 676 auto entity = GetEntity(data.data());
660 if (entity && entity->metadata()) { 677 if (entity && entity->metadata()) {
661 auto metadata = GetMetadata(entity->metadata()->Data()); 678 auto metadata = GetMetadata(entity->metadata()->Data());
@@ -664,6 +681,7 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid)
664 alreadyRemoved = true; 681 alreadyRemoved = true;
665 } 682 }
666 } 683 }
684 return true;
667 }, 685 },
668 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); 686 [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; });
669 if (!found) { 687 if (!found) {
diff --git a/common/storage/key.cpp b/common/storage/key.cpp
index 2327061..a6567ea 100644
--- a/common/storage/key.cpp
+++ b/common/storage/key.cpp
@@ -155,6 +155,11 @@ qint64 Revision::toQint64() const
155 return rev; 155 return rev;
156} 156}
157 157
158size_t Revision::toSizeT() const
159{
160 return rev;
161}
162
158bool Revision::isValidInternal(const QByteArray &bytes) 163bool Revision::isValidInternal(const QByteArray &bytes)
159{ 164{
160 if (bytes.size() != Revision::INTERNAL_REPR_SIZE) { 165 if (bytes.size() != Revision::INTERNAL_REPR_SIZE) {
diff --git a/common/storage/key.h b/common/storage/key.h
index acd81cf..da90ddd 100644
--- a/common/storage/key.h
+++ b/common/storage/key.h
@@ -75,6 +75,7 @@ public:
75 QByteArray toDisplayByteArray() const; 75 QByteArray toDisplayByteArray() const;
76 static Revision fromDisplayByteArray(const QByteArray &bytes); 76 static Revision fromDisplayByteArray(const QByteArray &bytes);
77 qint64 toQint64() const; 77 qint64 toQint64() const;
78 size_t toSizeT() const;
78 79
79 static bool isValidInternal(const QByteArray &); 80 static bool isValidInternal(const QByteArray &);
80 static bool isValidDisplay(const QByteArray &); 81 static bool isValidDisplay(const QByteArray &);
diff --git a/common/storage_common.cpp b/common/storage_common.cpp
index 264f223..ac246b2 100644
--- a/common/storage_common.cpp
+++ b/common/storage_common.cpp
@@ -117,26 +117,59 @@ qint64 DataStore::cleanedUpRevision(const DataStore::Transaction &transaction)
117 return r; 117 return r;
118} 118}
119 119
120QByteArray DataStore::getUidFromRevision(const DataStore::Transaction &transaction, qint64 revision) 120QByteArray DataStore::getUidFromRevision(const DataStore::Transaction &transaction, size_t revision)
121{ 121{
122 QByteArray uid; 122 QByteArray uid;
123 transaction.openDatabase("revisions") 123 transaction
124 .scan(QByteArray::number(revision), 124 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys)
125 [&](const QByteArray &, const QByteArray &value) -> bool { 125 .scan(revision,
126 uid = QByteArray{value.constData(), value.size()}; 126 [&](const size_t, const QByteArray &value) -> bool {
127 uid = QByteArray{ value.constData(), value.size() };
127 return false; 128 return false;
128 }, 129 },
129 [revision](const Error &error) { SinkWarning() << "Couldn't find uid for revision: " << revision << error.message; }); 130 [revision](const Error &error) {
131 SinkWarning() << "Couldn't find uid for revision: " << revision << error.message;
132 });
130 Q_ASSERT(!uid.isEmpty()); 133 Q_ASSERT(!uid.isEmpty());
131 return uid; 134 return uid;
132} 135}
133 136
134QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transaction, qint64 revision) 137#include <stdlib.h>
138
139size_t DataStore::getLatestRevisionFromUid(DataStore::Transaction &t, const QByteArray &uid)
140{
141 size_t revision;
142 t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues)
143 .findLatest(uid, [&revision](const QByteArray &key, const QByteArray &value) {
144 revision = *reinterpret_cast<const size_t *>(value.constData());
145 });
146
147 return revision;
148}
149
150QList<size_t> DataStore::getRevisionsUntilFromUid(DataStore::Transaction &t, const QByteArray &uid, size_t lastRevision)
151{
152 QList<size_t> queriedRevisions;
153 t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues)
154 .scan(uid, [&queriedRevisions, lastRevision](const QByteArray &, const QByteArray &value) {
155 size_t currentRevision = *reinterpret_cast<const size_t *>(value.constData());
156 if (currentRevision < lastRevision) {
157 queriedRevisions << currentRevision;
158 return true;
159 }
160
161 return false;
162 });
163
164 return queriedRevisions;
165}
166
167QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transaction, size_t revision)
135{ 168{
136 QByteArray type; 169 QByteArray type;
137 transaction.openDatabase("revisionType") 170 transaction.openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys)
138 .scan(QByteArray::number(revision), 171 .scan(revision,
139 [&](const QByteArray &, const QByteArray &value) -> bool { 172 [&](const size_t, const QByteArray &value) -> bool {
140 type = QByteArray{value.constData(), value.size()}; 173 type = QByteArray{value.constData(), value.size()};
141 return false; 174 return false;
142 }, 175 },
@@ -145,17 +178,31 @@ QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transact
145 return type; 178 return type;
146} 179}
147 180
148void DataStore::recordRevision(DataStore::Transaction &transaction, qint64 revision, const QByteArray &uid, const QByteArray &type) 181void DataStore::recordRevision(DataStore::Transaction &transaction, size_t revision,
182 const QByteArray &uid, const QByteArray &type)
149{ 183{
150 // TODO use integerkeys 184 transaction
151 transaction.openDatabase("revisions").write(QByteArray::number(revision), uid); 185 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys)
152 transaction.openDatabase("revisionType").write(QByteArray::number(revision), type); 186 .write(revision, uid);
187 transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues)
188 .write(uid, QByteArray::fromRawData(reinterpret_cast<const char *>(&revision), sizeof(revision)));
189 transaction
190 .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys)
191 .write(revision, type);
153} 192}
154 193
155void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revision) 194void DataStore::removeRevision(DataStore::Transaction &transaction, size_t revision)
156{ 195{
157 transaction.openDatabase("revisions").remove(QByteArray::number(revision)); 196 const QByteArray uid = getUidFromRevision(transaction, revision);
158 transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); 197
198 transaction
199 .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys)
200 .remove(revision);
201 transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues)
202 .remove(uid, QByteArray::fromRawData(reinterpret_cast<const char *>(&revision), sizeof(revision)));
203 transaction
204 .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys)
205 .remove(revision);
159} 206}
160 207
161void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) 208void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type)
@@ -207,7 +254,7 @@ DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t
207 Q_ASSERT(false); 254 Q_ASSERT(false);
208 return {}; 255 return {};
209 } 256 }
210 return t.openDatabase(type + ".main"); 257 return t.openDatabase(type + ".main", /* errorHandler= */ {}, IntegerKeys);
211} 258}
212 259
213bool DataStore::NamedDatabase::contains(const QByteArray &uid) 260bool DataStore::NamedDatabase::contains(const QByteArray &uid)
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index a007405..e3377b2 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -48,6 +48,10 @@ static QMutex sCreateDbiLock;
48static QHash<QString, MDB_env *> sEnvironments; 48static QHash<QString, MDB_env *> sEnvironments;
49static QHash<QString, MDB_dbi> sDbis; 49static QHash<QString, MDB_dbi> sDbis;
50 50
51int AllowDuplicates = MDB_DUPSORT;
52int IntegerKeys = MDB_INTEGERKEY;
53int IntegerValues = MDB_INTEGERDUP;
54
51int getErrorCode(int e) 55int getErrorCode(int e)
52{ 56{
53 switch (e) { 57 switch (e) {
@@ -101,14 +105,8 @@ static QList<QByteArray> getDatabaseNames(MDB_txn *transaction)
101 * and we always need to commit the transaction ASAP 105 * and we always need to commit the transaction ASAP
102 * We can only ever enter from one point per process. 106 * We can only ever enter from one point per process.
103 */ 107 */
104static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly, bool allowDuplicates, MDB_dbi &dbi) 108static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly, int flags, MDB_dbi &dbi)
105{ 109{
106
107 unsigned int flags = 0;
108 if (allowDuplicates) {
109 flags |= MDB_DUPSORT;
110 }
111
112 MDB_dbi flagtableDbi; 110 MDB_dbi flagtableDbi;
113 if (const int rc = mdb_dbi_open(transaction, "__flagtable", readOnly ? 0 : MDB_CREATE, &flagtableDbi)) { 111 if (const int rc = mdb_dbi_open(transaction, "__flagtable", readOnly ? 0 : MDB_CREATE, &flagtableDbi)) {
114 if (!readOnly) { 112 if (!readOnly) {
@@ -130,6 +128,10 @@ static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly,
130 } 128 }
131 } 129 }
132 130
131 if (flags & IntegerValues && !(flags & AllowDuplicates)) {
132 SinkWarning() << "Opening a database with integer values, but not duplicate keys";
133 }
134
133 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) { 135 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) {
134 //Create the db if it is not existing already 136 //Create the db if it is not existing already
135 if (rc == MDB_NOTFOUND && !readOnly) { 137 if (rc == MDB_NOTFOUND && !readOnly) {
@@ -165,7 +167,7 @@ static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly,
165 //Store the flags without the create option 167 //Store the flags without the create option
166 const auto ba = QByteArray::number(flags); 168 const auto ba = QByteArray::number(flags);
167 value.mv_data = const_cast<void*>(static_cast<const void*>(ba.constData())); 169 value.mv_data = const_cast<void*>(static_cast<const void*>(ba.constData()));
168 value.mv_size = db.size(); 170 value.mv_size = ba.size();
169 if (const int rc = mdb_put(transaction, flagtableDbi, &key, &value, MDB_NOOVERWRITE)) { 171 if (const int rc = mdb_put(transaction, flagtableDbi, &key, &value, MDB_NOOVERWRITE)) {
170 //We expect this to fail if we're only creating the dbi but not the db 172 //We expect this to fail if we're only creating the dbi but not the db
171 if (rc != MDB_KEYEXIST) { 173 if (rc != MDB_KEYEXIST) {
@@ -175,7 +177,7 @@ static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly,
175 } else { 177 } else {
176 //It's not an error if we only want to read 178 //It's not an error if we only want to read
177 if (!readOnly) { 179 if (!readOnly) {
178 SinkWarning() << "Failed to open db " << QByteArray(mdb_strerror(rc)); 180 SinkWarning() << "Failed to open db " << db << "error:" << QByteArray(mdb_strerror(rc));
179 return true; 181 return true;
180 } 182 }
181 return false; 183 return false;
@@ -187,8 +189,14 @@ static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly,
187class DataStore::NamedDatabase::Private 189class DataStore::NamedDatabase::Private
188{ 190{
189public: 191public:
190 Private(const QByteArray &_db, bool _allowDuplicates, const std::function<void(const DataStore::Error &error)> &_defaultErrorHandler, const QString &_name, MDB_txn *_txn) 192 Private(const QByteArray &_db, int _flags,
191 : db(_db), transaction(_txn), allowDuplicates(_allowDuplicates), defaultErrorHandler(_defaultErrorHandler), name(_name) 193 const std::function<void(const DataStore::Error &error)> &_defaultErrorHandler,
194 const QString &_name, MDB_txn *_txn)
195 : db(_db),
196 transaction(_txn),
197 flags(_flags),
198 defaultErrorHandler(_defaultErrorHandler),
199 name(_name)
192 { 200 {
193 } 201 }
194 202
@@ -199,7 +207,7 @@ public:
199 QByteArray db; 207 QByteArray db;
200 MDB_txn *transaction; 208 MDB_txn *transaction;
201 MDB_dbi dbi; 209 MDB_dbi dbi;
202 bool allowDuplicates; 210 int flags;
203 std::function<void(const DataStore::Error &error)> defaultErrorHandler; 211 std::function<void(const DataStore::Error &error)> defaultErrorHandler;
204 QString name; 212 QString name;
205 bool createdNewDbi = false; 213 bool createdNewDbi = false;
@@ -313,7 +321,7 @@ public:
313 } else { 321 } else {
314 dbiTransaction = transaction; 322 dbiTransaction = transaction;
315 } 323 }
316 if (createDbi(dbiTransaction, db, readOnly, allowDuplicates, dbi)) { 324 if (createDbi(dbiTransaction, db, readOnly, flags, dbi)) {
317 if (readOnly) { 325 if (readOnly) {
318 mdb_txn_commit(dbiTransaction); 326 mdb_txn_commit(dbiTransaction);
319 Q_ASSERT(!sDbis.contains(dbiName)); 327 Q_ASSERT(!sDbis.contains(dbiName));
@@ -371,6 +379,13 @@ DataStore::NamedDatabase::~NamedDatabase()
371 delete d; 379 delete d;
372} 380}
373 381
382bool DataStore::NamedDatabase::write(const size_t key, const QByteArray &value,
383 const std::function<void(const DataStore::Error &error)> &errorHandler)
384{
385 auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key));
386 return write(baKey, value, errorHandler);
387}
388
374bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) 389bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler)
375{ 390{
376 if (!d || !d->transaction) { 391 if (!d || !d->transaction) {
@@ -407,11 +422,25 @@ bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &s
407 return !rc; 422 return !rc;
408} 423}
409 424
425void DataStore::NamedDatabase::remove(
426 const size_t key, const std::function<void(const DataStore::Error &error)> &errorHandler)
427{
428 auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key));
429 return remove(baKey, errorHandler);
430}
431
410void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) 432void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler)
411{ 433{
412 remove(k, QByteArray(), errorHandler); 434 remove(k, QByteArray(), errorHandler);
413} 435}
414 436
437void DataStore::NamedDatabase::remove(const size_t key, const QByteArray &value,
438 const std::function<void(const DataStore::Error &error)> &errorHandler)
439{
440 auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key));
441 return remove(baKey, value, errorHandler);
442}
443
415void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) 444void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler)
416{ 445{
417 if (!d || !d->transaction) { 446 if (!d || !d->transaction) {
@@ -445,6 +474,19 @@ void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &val
445 } 474 }
446} 475}
447 476
477int DataStore::NamedDatabase::scan(const size_t key,
478 const std::function<bool(size_t key, const QByteArray &value)> &resultHandler,
479 const std::function<void(const DataStore::Error &error)> &errorHandler, bool skipInternalKeys) const
480{
481 auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key));
482 return scan(baKey,
483 [&resultHandler](const QByteArray &key, const QByteArray &value) {
484 size_t integerKey = *reinterpret_cast<const size_t *>(key.constData());
485 return resultHandler(integerKey, value);
486 },
487 errorHandler, /* findSubstringKeys = */ false, skipInternalKeys);
488}
489
448int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, 490int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
449 const std::function<void(const DataStore::Error &error)> &errorHandler, bool findSubstringKeys, bool skipInternalKeys) const 491 const std::function<void(const DataStore::Error &error)> &errorHandler, bool findSubstringKeys, bool skipInternalKeys) const
450{ 492{
@@ -471,8 +513,10 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool
471 513
472 int numberOfRetrievedValues = 0; 514 int numberOfRetrievedValues = 0;
473 515
474 if (k.isEmpty() || d->allowDuplicates || findSubstringKeys) { 516 bool allowDuplicates = d->flags & AllowDuplicates;
475 MDB_cursor_op op = d->allowDuplicates ? MDB_SET : MDB_FIRST; 517
518 if (k.isEmpty() || allowDuplicates || findSubstringKeys) {
519 MDB_cursor_op op = allowDuplicates ? MDB_SET : MDB_FIRST;
476 if (findSubstringKeys) { 520 if (findSubstringKeys) {
477 op = MDB_SET_RANGE; 521 op = MDB_SET_RANGE;
478 } 522 }
@@ -490,7 +534,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool
490 key.mv_data = (void *)k.constData(); 534 key.mv_data = (void *)k.constData();
491 key.mv_size = k.size(); 535 key.mv_size = k.size();
492 } 536 }
493 MDB_cursor_op nextOp = (d->allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT; 537 MDB_cursor_op nextOp = (allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT;
494 while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) { 538 while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) {
495 const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size); 539 const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size);
496 // Every consequitive lookup simply iterates through the list 540 // Every consequitive lookup simply iterates through the list
@@ -602,6 +646,15 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi
602 return; 646 return;
603} 647}
604 648
649int DataStore::NamedDatabase::findAllInRange(const size_t lowerBound, const size_t upperBound,
650 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
651 const std::function<void(const DataStore::Error &error)> &errorHandler) const
652{
653 auto baLowerBound = QByteArray::fromRawData(reinterpret_cast<const char *>(&lowerBound), sizeof(size_t));
654 auto baUpperBound = QByteArray::fromRawData(reinterpret_cast<const char *>(&upperBound), sizeof(size_t));
655 return findAllInRange(baLowerBound, baUpperBound, resultHandler, errorHandler);
656}
657
605int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, 658int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound,
606 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, 659 const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler,
607 const std::function<void(const DataStore::Error &error)> &errorHandler) const 660 const std::function<void(const DataStore::Error &error)> &errorHandler) const
@@ -862,7 +915,8 @@ static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray
862 return !openedTheWrongDatabase; 915 return !openedTheWrongDatabase;
863} 916}
864 917
865DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const 918DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db,
919 const std::function<void(const DataStore::Error &error)> &errorHandler, int flags) const
866{ 920{
867 if (!d) { 921 if (!d) {
868 SinkError() << "Tried to open database on invalid transaction: " << db; 922 SinkError() << "Tried to open database on invalid transaction: " << db;
@@ -871,7 +925,8 @@ DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &
871 Q_ASSERT(d->transaction); 925 Q_ASSERT(d->transaction);
872 // We don't now if anything changed 926 // We don't now if anything changed
873 d->implicitCommit = true; 927 d->implicitCommit = true;
874 auto p = new DataStore::NamedDatabase::Private(db, allowDuplicates, d->defaultErrorHandler, d->name, d->transaction); 928 auto p = new DataStore::NamedDatabase::Private(
929 db, flags, d->defaultErrorHandler, d->name, d->transaction);
875 auto ret = p->openDatabase(d->requestedRead, errorHandler); 930 auto ret = p->openDatabase(d->requestedRead, errorHandler);
876 if (!ret) { 931 if (!ret) {
877 delete p; 932 delete p;
@@ -1049,11 +1104,11 @@ public:
1049 1104
1050 //Create dbis from the given layout. 1105 //Create dbis from the given layout.
1051 for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { 1106 for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) {
1052 const bool allowDuplicates = it.value(); 1107 const int flags = it.value();
1053 MDB_dbi dbi = 0; 1108 MDB_dbi dbi = 0;
1054 const auto db = it.key(); 1109 const auto db = it.key();
1055 const auto dbiName = name + db; 1110 const auto dbiName = name + db;
1056 if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { 1111 if (createDbi(transaction, db, readOnly, flags, dbi)) {
1057 sDbis.insert(dbiName, dbi); 1112 sDbis.insert(dbiName, dbi);
1058 } 1113 }
1059 } 1114 }
@@ -1063,8 +1118,8 @@ public:
1063 MDB_dbi dbi = 0; 1118 MDB_dbi dbi = 0;
1064 const auto dbiName = name + db; 1119 const auto dbiName = name + db;
1065 //We're going to load the flags anyways. 1120 //We're going to load the flags anyways.
1066 bool allowDuplicates = false; 1121 const int flags = 0;
1067 if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { 1122 if (createDbi(transaction, db, readOnly, flags, dbi)) {
1068 sDbis.insert(dbiName, dbi); 1123 sDbis.insert(dbiName, dbi);
1069 } 1124 }
1070 } 1125 }