diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/changereplay.cpp | 7 | ||||
-rw-r--r-- | common/domain/typeimplementations.cpp | 14 | ||||
-rw-r--r-- | common/domain/typeimplementations_p.h | 10 | ||||
-rw-r--r-- | common/index.cpp | 8 | ||||
-rw-r--r-- | common/mail/threadindexer.cpp | 4 | ||||
-rw-r--r-- | common/storage.h | 36 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 106 | ||||
-rw-r--r-- | common/storage/key.cpp | 5 | ||||
-rw-r--r-- | common/storage/key.h | 1 | ||||
-rw-r--r-- | common/storage_common.cpp | 83 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 101 |
11 files changed, 262 insertions, 113 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 bb3f455..d6b5b76 100644 --- a/common/domain/typeimplementations.cpp +++ b/common/domain/typeimplementations.cpp | |||
@@ -86,7 +86,7 @@ void TypeImplementation<Mail>::configure(TypeIndex &index) | |||
86 | 86 | ||
87 | QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() | 87 | QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() |
88 | { | 88 | { |
89 | return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", 0}}, MailIndexConfig::databases()); | 89 | return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", Storage::IntegerKeys}}, MailIndexConfig::databases()); |
90 | } | 90 | } |
91 | 91 | ||
92 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) | 92 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) |
@@ -129,7 +129,7 @@ void TypeImplementation<Folder>::configure(TypeIndex &index) | |||
129 | 129 | ||
130 | QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() | 130 | QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() |
131 | { | 131 | { |
132 | return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", 0}}, FolderIndexConfig::databases()); | 132 | return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", Storage::IntegerKeys}}, FolderIndexConfig::databases()); |
133 | } | 133 | } |
134 | 134 | ||
135 | void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) | 135 | void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) |
@@ -154,7 +154,7 @@ void TypeImplementation<Contact>::configure(TypeIndex &index) | |||
154 | 154 | ||
155 | QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() | 155 | QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() |
156 | { | 156 | { |
157 | return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", 0}}, ContactIndexConfig::databases()); | 157 | return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", Storage::IntegerKeys}}, ContactIndexConfig::databases()); |
158 | } | 158 | } |
159 | 159 | ||
160 | void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) | 160 | void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) |
@@ -182,7 +182,7 @@ void TypeImplementation<Addressbook>::configure(TypeIndex &index) | |||
182 | 182 | ||
183 | QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() | 183 | QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() |
184 | { | 184 | { |
185 | return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", 0}}, AddressbookIndexConfig::databases()); | 185 | return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", Storage::IntegerKeys}}, AddressbookIndexConfig::databases()); |
186 | } | 186 | } |
187 | 187 | ||
188 | void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) | 188 | void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) |
@@ -204,7 +204,7 @@ void TypeImplementation<Event>::configure(TypeIndex &index) | |||
204 | 204 | ||
205 | QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() | 205 | QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() |
206 | { | 206 | { |
207 | return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", 0}}, EventIndexConfig::databases()); | 207 | return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", Storage::IntegerKeys}}, EventIndexConfig::databases()); |
208 | } | 208 | } |
209 | 209 | ||
210 | void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) | 210 | void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) |
@@ -232,7 +232,7 @@ void TypeImplementation<Todo>::configure(TypeIndex &index) | |||
232 | 232 | ||
233 | QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases() | 233 | QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases() |
234 | { | 234 | { |
235 | return merge(QMap<QByteArray, int>{{QByteArray{Todo::name} + ".main", 0}}, TodoIndexConfig::databases()); | 235 | return merge(QMap<QByteArray, int>{{QByteArray{Todo::name} + ".main", Storage::IntegerKeys}}, TodoIndexConfig::databases()); |
236 | } | 236 | } |
237 | 237 | ||
238 | void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper) | 238 | void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper) |
@@ -263,7 +263,7 @@ void TypeImplementation<Calendar>::configure(TypeIndex &index) | |||
263 | 263 | ||
264 | QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases() | 264 | QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases() |
265 | { | 265 | { |
266 | return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", 0}}, CalendarIndexConfig::databases()); | 266 | return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", Storage::IntegerKeys}}, CalendarIndexConfig::databases()); |
267 | } | 267 | } |
268 | 268 | ||
269 | void TypeImplementation<Calendar>::configure(PropertyMapper &propertyMapper) | 269 | void 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 | ||
7 | Index::Index(const QString &storageRoot, const QString &dbName, const QString &indexName, Sink::Storage::DataStore::AccessMode mode) | 7 | Index::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 | ||
15 | Index::Index(const QString &storageRoot, const QString &name, Sink::Storage::DataStore::AccessMode mode) | 15 | Index::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 | ||
23 | Index::Index(const QString &storageRoot, const Sink::Storage::DbLayout &layout, Sink::Storage::DataStore::AccessMode mode) | 23 | Index::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 | ||
31 | Index::Index(const QByteArray &name, Sink::Storage::DataStore::Transaction &transaction) | 31 | Index::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 | ||
119 | QMap<QByteArray, int> ThreadIndexer::databases() | 119 | QMap<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 @@ | |||
32 | namespace Sink { | 32 | namespace Sink { |
33 | namespace Storage { | 33 | namespace Storage { |
34 | 34 | ||
35 | extern int AllowDuplicates; | ||
36 | extern int IntegerKeys; | ||
37 | // Only useful with AllowDuplicates | ||
38 | extern int IntegerValues; | ||
39 | |||
35 | struct SINK_EXPORT DbLayout { | 40 | struct 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 bb0967a..6231479 100644 --- a/common/storage/entitystore.cpp +++ b/common/storage/entitystore.cpp | |||
@@ -38,8 +38,9 @@ using namespace Sink::Storage; | |||
38 | 38 | ||
39 | static QMap<QByteArray, int> baseDbs() | 39 | static 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 key: " << key; | 461 | SinkTraceCtx(d->logCtx) << "Multiple revisions for key: " << key; |
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 | |||
492 | void EntityStore::readLatest(const QByteArray &type, const Identifier &id, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback) | 509 | void 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 | ||
158 | size_t Revision::toSizeT() const | ||
159 | { | ||
160 | return rev; | ||
161 | } | ||
162 | |||
158 | bool Revision::isValidInternal(const QByteArray &bytes) | 163 | bool 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 | ||
120 | QByteArray DataStore::getUidFromRevision(const DataStore::Transaction &transaction, qint64 revision) | 120 | QByteArray 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 | ||
134 | QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transaction, qint64 revision) | 137 | #include <stdlib.h> |
138 | |||
139 | size_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 | |||
150 | QList<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 | |||
167 | QByteArray 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 | ||
148 | void DataStore::recordRevision(DataStore::Transaction &transaction, qint64 revision, const QByteArray &uid, const QByteArray &type) | 181 | void 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 | ||
155 | void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revision) | 194 | void 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 | ||
161 | void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) | 208 | void 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 | ||
213 | bool DataStore::NamedDatabase::contains(const QByteArray &uid) | 260 | bool DataStore::NamedDatabase::contains(const QByteArray &uid) |
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 3fee2a9..e3377b2 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -48,6 +48,10 @@ static QMutex sCreateDbiLock; | |||
48 | static QHash<QString, MDB_env *> sEnvironments; | 48 | static QHash<QString, MDB_env *> sEnvironments; |
49 | static QHash<QString, MDB_dbi> sDbis; | 49 | static QHash<QString, MDB_dbi> sDbis; |
50 | 50 | ||
51 | int AllowDuplicates = MDB_DUPSORT; | ||
52 | int IntegerKeys = MDB_INTEGERKEY; | ||
53 | int IntegerValues = MDB_INTEGERDUP; | ||
54 | |||
51 | int getErrorCode(int e) | 55 | int 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 | */ |
104 | static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly, bool allowDuplicates, MDB_dbi &dbi) | 108 | static 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) { |
@@ -164,8 +166,8 @@ static bool createDbi(MDB_txn *transaction, const QByteArray &db, bool readOnly, | |||
164 | key.mv_size = db.size(); | 166 | key.mv_size = db.size(); |
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*>(db.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, | |||
187 | class DataStore::NamedDatabase::Private | 189 | class DataStore::NamedDatabase::Private |
188 | { | 190 | { |
189 | public: | 191 | public: |
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 | ||
382 | bool 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 | |||
374 | bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) | 389 | bool 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 | ||
425 | void 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 | |||
410 | void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) | 432 | void 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 | ||
437 | void 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 | |||
415 | void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) | 444 | void 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 | ||
477 | int 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 | |||
448 | int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | 490 | int 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 | ||
649 | int 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 | |||
605 | int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, | 658 | int 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 | ||
865 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const | 918 | DataStore::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 | } |