diff options
-rw-r--r-- | common/changereplay.cpp | 7 | ||||
-rw-r--r-- | common/domain/typeimplementations.cpp | 20 | ||||
-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 | 40 | ||||
-rw-r--r-- | common/storage/entitystore.cpp | 160 | ||||
-rw-r--r-- | common/storage/key.cpp | 5 | ||||
-rw-r--r-- | common/storage/key.h | 5 | ||||
-rw-r--r-- | common/storage_common.cpp | 105 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 136 | ||||
-rw-r--r-- | common/utils.cpp | 10 | ||||
-rw-r--r-- | common/utils.h | 14 | ||||
-rw-r--r-- | sinksh/syntax_modules/sink_inspect.cpp | 9 | ||||
-rw-r--r-- | tests/dbwriter.cpp | 12 | ||||
-rw-r--r-- | tests/entitystoretest.cpp | 95 | ||||
-rw-r--r-- | tests/pipelinetest.cpp | 33 | ||||
-rw-r--r-- | tests/storagetest.cpp | 260 |
18 files changed, 688 insertions, 245 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..6e14501 100644 --- a/common/domain/typeimplementations.cpp +++ b/common/domain/typeimplementations.cpp | |||
@@ -80,7 +80,11 @@ typedef IndexConfig<Calendar, | |||
80 | ValueIndex<Calendar::Name> | 80 | ValueIndex<Calendar::Name> |
81 | > CalendarIndexConfig; | 81 | > CalendarIndexConfig; |
82 | 82 | ||
83 | 83 | template <typename EntityType, typename EntityIndexConfig> | |
84 | QMap<QByteArray, int> defaultTypeDatabases() | ||
85 | { | ||
86 | return merge(QMap<QByteArray, int>{{QByteArray{EntityType::name} + ".main", Storage::IntegerKeys}}, EntityIndexConfig::databases()); | ||
87 | } | ||
84 | 88 | ||
85 | void TypeImplementation<Mail>::configure(TypeIndex &index) | 89 | void TypeImplementation<Mail>::configure(TypeIndex &index) |
86 | { | 90 | { |
@@ -89,7 +93,7 @@ void TypeImplementation<Mail>::configure(TypeIndex &index) | |||
89 | 93 | ||
90 | QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() | 94 | QMap<QByteArray, int> TypeImplementation<Mail>::typeDatabases() |
91 | { | 95 | { |
92 | return merge(QMap<QByteArray, int>{{QByteArray{Mail::name} + ".main", 0}}, MailIndexConfig::databases()); | 96 | return defaultTypeDatabases<Mail, MailIndexConfig>(); |
93 | } | 97 | } |
94 | 98 | ||
95 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) | 99 | void TypeImplementation<Mail>::configure(IndexPropertyMapper &indexPropertyMapper) |
@@ -132,7 +136,7 @@ void TypeImplementation<Folder>::configure(TypeIndex &index) | |||
132 | 136 | ||
133 | QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() | 137 | QMap<QByteArray, int> TypeImplementation<Folder>::typeDatabases() |
134 | { | 138 | { |
135 | return merge(QMap<QByteArray, int>{{QByteArray{Folder::name} + ".main", 0}}, FolderIndexConfig::databases()); | 139 | return defaultTypeDatabases<Folder, FolderIndexConfig>(); |
136 | } | 140 | } |
137 | 141 | ||
138 | void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) | 142 | void TypeImplementation<Folder>::configure(PropertyMapper &propertyMapper) |
@@ -157,7 +161,7 @@ void TypeImplementation<Contact>::configure(TypeIndex &index) | |||
157 | 161 | ||
158 | QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() | 162 | QMap<QByteArray, int> TypeImplementation<Contact>::typeDatabases() |
159 | { | 163 | { |
160 | return merge(QMap<QByteArray, int>{{QByteArray{Contact::name} + ".main", 0}}, ContactIndexConfig::databases()); | 164 | return defaultTypeDatabases<Contact, ContactIndexConfig>(); |
161 | } | 165 | } |
162 | 166 | ||
163 | void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) | 167 | void TypeImplementation<Contact>::configure(PropertyMapper &propertyMapper) |
@@ -185,7 +189,7 @@ void TypeImplementation<Addressbook>::configure(TypeIndex &index) | |||
185 | 189 | ||
186 | QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() | 190 | QMap<QByteArray, int> TypeImplementation<Addressbook>::typeDatabases() |
187 | { | 191 | { |
188 | return merge(QMap<QByteArray, int>{{QByteArray{Addressbook::name} + ".main", 0}}, AddressbookIndexConfig::databases()); | 192 | return defaultTypeDatabases<Addressbook, AddressbookIndexConfig>(); |
189 | } | 193 | } |
190 | 194 | ||
191 | void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) | 195 | void TypeImplementation<Addressbook>::configure(PropertyMapper &propertyMapper) |
@@ -207,7 +211,7 @@ void TypeImplementation<Event>::configure(TypeIndex &index) | |||
207 | 211 | ||
208 | QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() | 212 | QMap<QByteArray, int> TypeImplementation<Event>::typeDatabases() |
209 | { | 213 | { |
210 | return merge(QMap<QByteArray, int>{{QByteArray{Event::name} + ".main", 0}}, EventIndexConfig::databases()); | 214 | return defaultTypeDatabases<Event, EventIndexConfig>(); |
211 | } | 215 | } |
212 | 216 | ||
213 | void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) | 217 | void TypeImplementation<Event>::configure(PropertyMapper &propertyMapper) |
@@ -235,7 +239,7 @@ void TypeImplementation<Todo>::configure(TypeIndex &index) | |||
235 | 239 | ||
236 | QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases() | 240 | QMap<QByteArray, int> TypeImplementation<Todo>::typeDatabases() |
237 | { | 241 | { |
238 | return merge(QMap<QByteArray, int>{{QByteArray{Todo::name} + ".main", 0}}, TodoIndexConfig::databases()); | 242 | return defaultTypeDatabases<Todo, TodoIndexConfig>(); |
239 | } | 243 | } |
240 | 244 | ||
241 | void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper) | 245 | void TypeImplementation<Todo>::configure(PropertyMapper &propertyMapper) |
@@ -266,7 +270,7 @@ void TypeImplementation<Calendar>::configure(TypeIndex &index) | |||
266 | 270 | ||
267 | QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases() | 271 | QMap<QByteArray, int> TypeImplementation<Calendar>::typeDatabases() |
268 | { | 272 | { |
269 | return merge(QMap<QByteArray, int>{{QByteArray{Calendar::name} + ".main", 0}}, CalendarIndexConfig::databases()); | 273 | return defaultTypeDatabases<Calendar, CalendarIndexConfig>(); |
270 | } | 274 | } |
271 | 275 | ||
272 | void TypeImplementation<Calendar>::configure(PropertyMapper &propertyMapper) | 276 | 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..ac6509d 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,23 @@ 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 | // of QByteArray for keys | ||
89 | 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)>()); | ||
90 | |||
83 | /** | 91 | /** |
84 | * Remove a key | 92 | * Remove a key |
85 | */ | 93 | */ |
86 | void remove(const QByteArray &key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); | 94 | void remove(const QByteArray &key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); |
95 | |||
96 | void remove(const size_t key, const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()); | ||
97 | |||
87 | /** | 98 | /** |
88 | * Remove a key-value pair | 99 | * Remove a key-value pair |
89 | */ | 100 | */ |
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)>()); | 101 | 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 | 102 | ||
103 | 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)>()); | ||
104 | |||
92 | /** | 105 | /** |
93 | * Read values with a given key. | 106 | * Read values with a given key. |
94 | * | 107 | * |
@@ -101,6 +114,9 @@ public: | |||
101 | int scan(const QByteArray &key, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | 114 | 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; | 115 | const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>(), bool findSubstringKeys = false, bool skipInternalKeys = true) const; |
103 | 116 | ||
117 | int scan(const size_t key, const std::function<bool(size_t key, const QByteArray &value)> &resultHandler, | ||
118 | const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>(), bool skipInternalKeys = true) const; | ||
119 | |||
104 | /** | 120 | /** |
105 | * Finds the last value in a series matched by prefix. | 121 | * Finds the last value in a series matched by prefix. |
106 | * | 122 | * |
@@ -110,6 +126,9 @@ public: | |||
110 | void findLatest(const QByteArray &uid, const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | 126 | void findLatest(const QByteArray &uid, const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, |
111 | const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()) const; | 127 | const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()) const; |
112 | 128 | ||
129 | void findLatest(size_t key, const std::function<void(size_t key, const QByteArray &value)> &resultHandler, | ||
130 | const std::function<void(const DataStore::Error &error)> &errorHandler = std::function<void(const DataStore::Error &error)>()) const; | ||
131 | |||
113 | /** | 132 | /** |
114 | * Finds all the keys and values whose keys are in a given range | 133 | * Finds all the keys and values whose keys are in a given range |
115 | * (inclusive). | 134 | * (inclusive). |
@@ -119,6 +138,10 @@ public: | |||
119 | const std::function<void(const DataStore::Error &error)> &errorHandler = | 138 | const std::function<void(const DataStore::Error &error)> &errorHandler = |
120 | std::function<void(const DataStore::Error &error)>()) const; | 139 | std::function<void(const DataStore::Error &error)>()) const; |
121 | 140 | ||
141 | int findAllInRange(const size_t lowerBound, const size_t upperBound, | ||
142 | const std::function<void(size_t key, const QByteArray &value)> &resultHandler, | ||
143 | const std::function<void(const DataStore::Error &error)> &errorHandler = {}) const; | ||
144 | |||
122 | /** | 145 | /** |
123 | * Returns true if the database contains the substring key. | 146 | * Returns true if the database contains the substring key. |
124 | */ | 147 | */ |
@@ -163,8 +186,9 @@ public: | |||
163 | 186 | ||
164 | QList<QByteArray> getDatabaseNames() const; | 187 | QList<QByteArray> getDatabaseNames() const; |
165 | 188 | ||
166 | NamedDatabase openDatabase(const QByteArray &name = {"default"}, | 189 | NamedDatabase openDatabase(const QByteArray &name = { "default" }, |
167 | const std::function<void(const DataStore::Error &error)> &errorHandler = {}, bool allowDuplicates = false) const; | 190 | const std::function<void(const DataStore::Error &error)> &errorHandler = {}, |
191 | int flags = 0) const; | ||
168 | 192 | ||
169 | Transaction(Transaction &&other); | 193 | Transaction(Transaction &&other); |
170 | Transaction &operator=(Transaction &&other); | 194 | Transaction &operator=(Transaction &&other); |
@@ -224,13 +248,17 @@ public: | |||
224 | static qint64 cleanedUpRevision(const Transaction &); | 248 | static qint64 cleanedUpRevision(const Transaction &); |
225 | static void setCleanedUpRevision(Transaction &, qint64 revision); | 249 | static void setCleanedUpRevision(Transaction &, qint64 revision); |
226 | 250 | ||
227 | static QByteArray getUidFromRevision(const Transaction &, qint64 revision); | 251 | static QByteArray getUidFromRevision(const Transaction &, size_t revision); |
228 | static QByteArray getTypeFromRevision(const Transaction &, qint64 revision); | 252 | static size_t getLatestRevisionFromUid(Transaction &, const QByteArray &uid); |
229 | static void recordRevision(Transaction &, qint64 revision, const QByteArray &uid, const QByteArray &type); | 253 | static QList<size_t> getRevisionsUntilFromUid(DataStore::Transaction &, const QByteArray &uid, size_t lastRevision); |
230 | static void removeRevision(Transaction &, qint64 revision); | 254 | static QList<size_t> getRevisionsFromUid(DataStore::Transaction &, const QByteArray &uid); |
255 | static QByteArray getTypeFromRevision(const Transaction &, size_t revision); | ||
256 | static void recordRevision(Transaction &, size_t revision, const QByteArray &uid, const QByteArray &type); | ||
257 | static void removeRevision(Transaction &, size_t revision); | ||
231 | static void recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type); | 258 | 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); | 259 | 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)> &); | 260 | static void getUids(const QByteArray &type, const Transaction &, const std::function<void(const QByteArray &uid)> &); |
261 | static bool hasUid(const QByteArray &type, const Transaction &, const QByteArray &uid); | ||
234 | 262 | ||
235 | bool exists() const; | 263 | bool exists() const; |
236 | static bool exists(const QString &storageRoot, const QString &name); | 264 | static bool exists(const QString &storageRoot, const QString &name); |
diff --git a/common/storage/entitystore.cpp b/common/storage/entitystore.cpp index 276ee6a..454e25a 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: " << key << "of type:" << type; |
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 | ||
@@ -433,20 +440,12 @@ QVector<Identifier> EntityStore::fullScan(const QByteArray &type) | |||
433 | SinkTraceCtx(d->logCtx) << "Database is not existing: " << type; | 440 | SinkTraceCtx(d->logCtx) << "Database is not existing: " << type; |
434 | return {}; | 441 | return {}; |
435 | } | 442 | } |
436 | //The scan can return duplicate results if we have multiple revisions, so we use a set to deduplicate. | 443 | |
437 | QSet<Identifier> keys; | 444 | QSet<Identifier> keys; |
438 | DataStore::mainDatabase(d->getTransaction(), type) | 445 | |
439 | .scan(QByteArray(), | 446 | DataStore::getUids(type, d->getTransaction(), [&keys] (const QByteArray &uid) { |
440 | [&](const QByteArray &key, const QByteArray &value) -> bool { | 447 | keys << Identifier::fromDisplayByteArray(uid); |
441 | const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier(); | 448 | }); |
442 | if (keys.contains(uid)) { | ||
443 | //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."; | ||
445 | } | ||
446 | keys << uid; | ||
447 | return true; | ||
448 | }, | ||
449 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during fullScan query: " << error.message; }); | ||
450 | 449 | ||
451 | SinkTraceCtx(d->logCtx) << "Full scan retrieved " << keys.size() << " results."; | 450 | SinkTraceCtx(d->logCtx) << "Full scan retrieved " << keys.size() << " results."; |
452 | return keys.toList().toVector(); | 451 | return keys.toList().toVector(); |
@@ -492,12 +491,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) | 491 | void EntityStore::readLatest(const QByteArray &type, const Identifier &id, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback) |
493 | { | 492 | { |
494 | Q_ASSERT(d); | 493 | Q_ASSERT(d); |
495 | const auto internalKey = id.toInternalByteArray(); | 494 | const size_t revision = DataStore::getLatestRevisionFromUid(d->getTransaction(), id.toDisplayByteArray()); |
496 | auto db = DataStore::mainDatabase(d->getTransaction(), type); | 495 | auto db = DataStore::mainDatabase(d->getTransaction(), type); |
497 | db.findLatest(internalKey, | 496 | db.scan(revision, |
498 | [=](const QByteArray &key, const QByteArray &value) { | 497 | [=](size_t, const QByteArray &value) { |
499 | const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier().toDisplayByteArray(); | 498 | callback(id.toDisplayByteArray(), Sink::EntityBuffer(value.data(), value.size())); |
500 | callback(uid, Sink::EntityBuffer(value.data(), value.size())); | 499 | return false; |
501 | }, | 500 | }, |
502 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during readLatest query: " << error.message << id; }); | 501 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error during readLatest query: " << error.message << id; }); |
503 | } | 502 | } |
@@ -546,9 +545,9 @@ void EntityStore::readEntity(const QByteArray &type, const QByteArray &displayKe | |||
546 | { | 545 | { |
547 | const auto key = Key::fromDisplayByteArray(displayKey); | 546 | const auto key = Key::fromDisplayByteArray(displayKey); |
548 | auto db = DataStore::mainDatabase(d->getTransaction(), type); | 547 | auto db = DataStore::mainDatabase(d->getTransaction(), type); |
549 | db.scan(key.toInternalByteArray(), | 548 | db.scan(key.revision().toSizeT(), |
550 | [=](const QByteArray &key, const QByteArray &value) -> bool { | 549 | [=](size_t rev, const QByteArray &value) -> bool { |
551 | const auto uid = Sink::Storage::Key::fromInternalByteArray(key).identifier().toDisplayByteArray(); | 550 | const auto uid = DataStore::getUidFromRevision(d->transaction, rev); |
552 | callback(uid, Sink::EntityBuffer(value.data(), value.size())); | 551 | callback(uid, Sink::EntityBuffer(value.data(), value.size())); |
553 | return false; | 552 | return false; |
554 | }, | 553 | }, |
@@ -604,18 +603,8 @@ void EntityStore::readRevisions(qint64 baseRevision, const QByteArray &expectedT | |||
604 | 603 | ||
605 | void EntityStore::readPrevious(const QByteArray &type, const Identifier &id, qint64 revision, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback) | 604 | void EntityStore::readPrevious(const QByteArray &type, const Identifier &id, qint64 revision, const std::function<void(const QByteArray &uid, const EntityBuffer &entity)> callback) |
606 | { | 605 | { |
607 | auto db = DataStore::mainDatabase(d->getTransaction(), type); | 606 | const auto previousRevisions = DataStore::getRevisionsUntilFromUid(d->getTransaction(), id.toDisplayByteArray(), revision); |
608 | qint64 latestRevision = 0; | 607 | const size_t latestRevision = previousRevisions[previousRevisions.size() - 1]; |
609 | const auto internalUid = id.toInternalByteArray(); | ||
610 | db.scan(internalUid, | ||
611 | [&latestRevision, revision](const QByteArray &key, const QByteArray &) -> bool { | ||
612 | const auto foundRevision = Key::fromInternalByteArray(key).revision().toQint64(); | ||
613 | if (foundRevision < revision && foundRevision > latestRevision) { | ||
614 | latestRevision = foundRevision; | ||
615 | } | ||
616 | return true; | ||
617 | }, | ||
618 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read current value from storage: " << error.message; }, true); | ||
619 | const auto key = Key(id, latestRevision); | 608 | const auto key = Key(id, latestRevision); |
620 | readEntity(type, key.toDisplayByteArray(), callback); | 609 | readEntity(type, key.toDisplayByteArray(), callback); |
621 | } | 610 | } |
@@ -641,21 +630,20 @@ void EntityStore::readAllUids(const QByteArray &type, const std::function<void(c | |||
641 | DataStore::getUids(type, d->getTransaction(), callback); | 630 | DataStore::getUids(type, d->getTransaction(), callback); |
642 | } | 631 | } |
643 | 632 | ||
644 | bool EntityStore::contains(const QByteArray &type, const QByteArray &uid) | 633 | bool EntityStore::contains(const QByteArray & /* type */, const QByteArray &uid) |
645 | { | 634 | { |
646 | Q_ASSERT(!uid.isEmpty()); | 635 | Q_ASSERT(!uid.isEmpty()); |
647 | const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray(); | 636 | return !DataStore::getRevisionsFromUid(d->getTransaction(), uid).isEmpty(); |
648 | return DataStore::mainDatabase(d->getTransaction(), type).contains(internalUid); | ||
649 | } | 637 | } |
650 | 638 | ||
651 | bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | 639 | bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) |
652 | { | 640 | { |
653 | bool found = false; | 641 | bool found = false; |
654 | bool alreadyRemoved = false; | 642 | bool alreadyRemoved = false; |
655 | const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray(); | 643 | const size_t revision = DataStore::getLatestRevisionFromUid(d->getTransaction(), uid); |
656 | DataStore::mainDatabase(d->transaction, type) | 644 | DataStore::mainDatabase(d->transaction, type) |
657 | .findLatest(internalUid, | 645 | .scan(revision, |
658 | [&found, &alreadyRemoved](const QByteArray &key, const QByteArray &data) { | 646 | [&found, &alreadyRemoved](size_t, const QByteArray &data) { |
659 | auto entity = GetEntity(data.data()); | 647 | auto entity = GetEntity(data.data()); |
660 | if (entity && entity->metadata()) { | 648 | if (entity && entity->metadata()) { |
661 | auto metadata = GetMetadata(entity->metadata()->Data()); | 649 | auto metadata = GetMetadata(entity->metadata()->Data()); |
@@ -664,6 +652,7 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | |||
664 | alreadyRemoved = true; | 652 | alreadyRemoved = true; |
665 | } | 653 | } |
666 | } | 654 | } |
655 | return true; | ||
667 | }, | 656 | }, |
668 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); | 657 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Failed to read old revision from storage: " << error.message; }); |
669 | if (!found) { | 658 | if (!found) { |
@@ -677,23 +666,32 @@ bool EntityStore::exists(const QByteArray &type, const QByteArray &uid) | |||
677 | return true; | 666 | return true; |
678 | } | 667 | } |
679 | 668 | ||
680 | void EntityStore::readRevisions(const QByteArray &type, const QByteArray &uid, qint64 startingRevision, const std::function<void(const QByteArray &uid, qint64 revision, const EntityBuffer &entity)> callback) | 669 | void EntityStore::readRevisions(const QByteArray &type, const QByteArray &uid, qint64 startingRevision, |
670 | const std::function<void(const QByteArray &uid, qint64 revision, const EntityBuffer &entity)> callback) | ||
681 | { | 671 | { |
682 | Q_ASSERT(d); | 672 | Q_ASSERT(d); |
683 | Q_ASSERT(!uid.isEmpty()); | 673 | Q_ASSERT(!uid.isEmpty()); |
684 | const auto internalUid = Identifier::fromDisplayByteArray(uid).toInternalByteArray(); | ||
685 | DataStore::mainDatabase(d->transaction, type) | ||
686 | .scan(internalUid, | ||
687 | [&](const QByteArray &key, const QByteArray &value) -> bool { | ||
688 | const auto parsedKey = Key::fromInternalByteArray(key); | ||
689 | const auto revision = parsedKey.revision().toQint64(); | ||
690 | if (revision >= startingRevision) { | ||
691 | callback(parsedKey.identifier().toDisplayByteArray(), revision, Sink::EntityBuffer(value.data(), value.size())); | ||
692 | } | ||
693 | return true; | ||
694 | }, | ||
695 | [&](const DataStore::Error &error) { SinkWarningCtx(d->logCtx) << "Error while reading: " << error.message; }, true); | ||
696 | 674 | ||
675 | const auto revisions = DataStore::getRevisionsFromUid(d->transaction, uid); | ||
676 | |||
677 | const auto db = DataStore::mainDatabase(d->transaction, type); | ||
678 | |||
679 | for (const auto revision : revisions) { | ||
680 | if (revision < startingRevision) { | ||
681 | continue; | ||
682 | } | ||
683 | |||
684 | db.scan(revision, | ||
685 | [&](size_t rev, const QByteArray &value) { | ||
686 | Q_ASSERT(rev == revision); | ||
687 | callback(uid, revision, Sink::EntityBuffer(value.data(), value.size())); | ||
688 | return false; | ||
689 | }, | ||
690 | [&](const DataStore::Error &error) { | ||
691 | SinkWarningCtx(d->logCtx) << "Error while reading: " << error.message; | ||
692 | }, | ||
693 | true); | ||
694 | } | ||
697 | } | 695 | } |
698 | 696 | ||
699 | qint64 EntityStore::maxRevision() | 697 | qint64 EntityStore::maxRevision() |
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 baabe38..da90ddd 100644 --- a/common/storage/key.h +++ b/common/storage/key.h | |||
@@ -67,7 +67,7 @@ public: | |||
67 | static const constexpr size_t INTERNAL_REPR_SIZE = 19; | 67 | static const constexpr size_t INTERNAL_REPR_SIZE = 19; |
68 | static const constexpr size_t DISPLAY_REPR_SIZE = 19; | 68 | static const constexpr size_t DISPLAY_REPR_SIZE = 19; |
69 | 69 | ||
70 | Revision(qint64 rev) : rev(rev) {} | 70 | Revision(size_t rev) : rev(rev) {} |
71 | 71 | ||
72 | QByteArray toInternalByteArray() const; | 72 | QByteArray toInternalByteArray() const; |
73 | static Revision fromInternalByteArray(const QByteArray &bytes); | 73 | static Revision fromInternalByteArray(const QByteArray &bytes); |
@@ -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 &); |
@@ -84,7 +85,7 @@ public: | |||
84 | bool operator!=(const Revision &other) const; | 85 | bool operator!=(const Revision &other) const; |
85 | 86 | ||
86 | private: | 87 | private: |
87 | qint64 rev; | 88 | size_t rev; |
88 | }; | 89 | }; |
89 | 90 | ||
90 | class Key | 91 | class Key |
diff --git a/common/storage_common.cpp b/common/storage_common.cpp index 264f223..7c794c3 100644 --- a/common/storage_common.cpp +++ b/common/storage_common.cpp | |||
@@ -117,26 +117,69 @@ 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 | size_t DataStore::getLatestRevisionFromUid(DataStore::Transaction &t, const QByteArray &uid) |
138 | { | ||
139 | size_t revision; | ||
140 | t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues) | ||
141 | .findLatest(uid, [&revision](const QByteArray &key, const QByteArray &value) { | ||
142 | revision = byteArrayToSizeT(value); | ||
143 | }); | ||
144 | |||
145 | return revision; | ||
146 | } | ||
147 | |||
148 | QList<size_t> DataStore::getRevisionsUntilFromUid(DataStore::Transaction &t, const QByteArray &uid, size_t lastRevision) | ||
149 | { | ||
150 | QList<size_t> queriedRevisions; | ||
151 | t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues) | ||
152 | .scan(uid, [&queriedRevisions, lastRevision](const QByteArray &, const QByteArray &value) { | ||
153 | size_t currentRevision = byteArrayToSizeT(value); | ||
154 | if (currentRevision < lastRevision) { | ||
155 | queriedRevisions << currentRevision; | ||
156 | return true; | ||
157 | } | ||
158 | |||
159 | return false; | ||
160 | }); | ||
161 | |||
162 | return queriedRevisions; | ||
163 | } | ||
164 | |||
165 | QList<size_t> DataStore::getRevisionsFromUid(DataStore::Transaction &t, const QByteArray &uid) | ||
166 | { | ||
167 | QList<size_t> queriedRevisions; | ||
168 | t.openDatabase("uidsToRevisions", {}, AllowDuplicates | IntegerValues) | ||
169 | .scan(uid, [&queriedRevisions](const QByteArray &, const QByteArray &value) { | ||
170 | queriedRevisions << byteArrayToSizeT(value); | ||
171 | return true; | ||
172 | }); | ||
173 | |||
174 | return queriedRevisions; | ||
175 | } | ||
176 | |||
177 | QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transaction, size_t revision) | ||
135 | { | 178 | { |
136 | QByteArray type; | 179 | QByteArray type; |
137 | transaction.openDatabase("revisionType") | 180 | transaction.openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) |
138 | .scan(QByteArray::number(revision), | 181 | .scan(revision, |
139 | [&](const QByteArray &, const QByteArray &value) -> bool { | 182 | [&](const size_t, const QByteArray &value) -> bool { |
140 | type = QByteArray{value.constData(), value.size()}; | 183 | type = QByteArray{value.constData(), value.size()}; |
141 | return false; | 184 | return false; |
142 | }, | 185 | }, |
@@ -145,17 +188,31 @@ QByteArray DataStore::getTypeFromRevision(const DataStore::Transaction &transact | |||
145 | return type; | 188 | return type; |
146 | } | 189 | } |
147 | 190 | ||
148 | void DataStore::recordRevision(DataStore::Transaction &transaction, qint64 revision, const QByteArray &uid, const QByteArray &type) | 191 | void DataStore::recordRevision(DataStore::Transaction &transaction, size_t revision, |
192 | const QByteArray &uid, const QByteArray &type) | ||
149 | { | 193 | { |
150 | // TODO use integerkeys | 194 | transaction |
151 | transaction.openDatabase("revisions").write(QByteArray::number(revision), uid); | 195 | .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys) |
152 | transaction.openDatabase("revisionType").write(QByteArray::number(revision), type); | 196 | .write(revision, uid); |
197 | transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues) | ||
198 | .write(uid, sizeTToByteArray(revision)); | ||
199 | transaction | ||
200 | .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) | ||
201 | .write(revision, type); | ||
153 | } | 202 | } |
154 | 203 | ||
155 | void DataStore::removeRevision(DataStore::Transaction &transaction, qint64 revision) | 204 | void DataStore::removeRevision(DataStore::Transaction &transaction, size_t revision) |
156 | { | 205 | { |
157 | transaction.openDatabase("revisions").remove(QByteArray::number(revision)); | 206 | const QByteArray uid = getUidFromRevision(transaction, revision); |
158 | transaction.openDatabase("revisionType").remove(QByteArray::number(revision)); | 207 | |
208 | transaction | ||
209 | .openDatabase("revisions", /* errorHandler = */ {}, IntegerKeys) | ||
210 | .remove(revision); | ||
211 | transaction.openDatabase("uidsToRevisions", /* errorHandler = */ {}, AllowDuplicates | IntegerValues) | ||
212 | .remove(uid, sizeTToByteArray(revision)); | ||
213 | transaction | ||
214 | .openDatabase("revisionType", /* errorHandler = */ {}, IntegerKeys) | ||
215 | .remove(revision); | ||
159 | } | 216 | } |
160 | 217 | ||
161 | void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) | 218 | void DataStore::recordUid(DataStore::Transaction &transaction, const QByteArray &uid, const QByteArray &type) |
@@ -176,6 +233,18 @@ void DataStore::getUids(const QByteArray &type, const Transaction &transaction, | |||
176 | }); | 233 | }); |
177 | } | 234 | } |
178 | 235 | ||
236 | bool DataStore::hasUid(const QByteArray &type, const Transaction &transaction, const QByteArray &uid) | ||
237 | { | ||
238 | bool hasTheUid = false; | ||
239 | transaction.openDatabase(type + "uids").scan(uid, [&](const QByteArray &key, const QByteArray &) { | ||
240 | Q_ASSERT(uid == key); | ||
241 | hasTheUid = true; | ||
242 | return false; | ||
243 | }); | ||
244 | |||
245 | return hasTheUid; | ||
246 | } | ||
247 | |||
179 | bool DataStore::isInternalKey(const char *key) | 248 | bool DataStore::isInternalKey(const char *key) |
180 | { | 249 | { |
181 | return key && strncmp(key, s_internalPrefix, s_internalPrefixSize) == 0; | 250 | return key && strncmp(key, s_internalPrefix, s_internalPrefixSize) == 0; |
@@ -207,7 +276,7 @@ DataStore::NamedDatabase DataStore::mainDatabase(const DataStore::Transaction &t | |||
207 | Q_ASSERT(false); | 276 | Q_ASSERT(false); |
208 | return {}; | 277 | return {}; |
209 | } | 278 | } |
210 | return t.openDatabase(type + ".main"); | 279 | return t.openDatabase(type + ".main", /* errorHandler= */ {}, IntegerKeys); |
211 | } | 280 | } |
212 | 281 | ||
213 | bool DataStore::NamedDatabase::contains(const QByteArray &uid) | 282 | bool DataStore::NamedDatabase::contains(const QByteArray &uid) |
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index a007405..0458dae 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) { |
@@ -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, | |||
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,12 @@ 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 | return write(sizeTToByteArray(key), value, errorHandler); | ||
386 | } | ||
387 | |||
374 | bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) | 388 | bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) |
375 | { | 389 | { |
376 | if (!d || !d->transaction) { | 390 | if (!d || !d->transaction) { |
@@ -407,11 +421,23 @@ bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &s | |||
407 | return !rc; | 421 | return !rc; |
408 | } | 422 | } |
409 | 423 | ||
424 | void DataStore::NamedDatabase::remove( | ||
425 | const size_t key, const std::function<void(const DataStore::Error &error)> &errorHandler) | ||
426 | { | ||
427 | return remove(sizeTToByteArray(key), errorHandler); | ||
428 | } | ||
429 | |||
410 | void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) | 430 | void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) |
411 | { | 431 | { |
412 | remove(k, QByteArray(), errorHandler); | 432 | remove(k, QByteArray(), errorHandler); |
413 | } | 433 | } |
414 | 434 | ||
435 | void DataStore::NamedDatabase::remove(const size_t key, const QByteArray &value, | ||
436 | const std::function<void(const DataStore::Error &error)> &errorHandler) | ||
437 | { | ||
438 | return remove(sizeTToByteArray(key), value, errorHandler); | ||
439 | } | ||
440 | |||
415 | void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) | 441 | void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) |
416 | { | 442 | { |
417 | if (!d || !d->transaction) { | 443 | if (!d || !d->transaction) { |
@@ -445,6 +471,17 @@ void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &val | |||
445 | } | 471 | } |
446 | } | 472 | } |
447 | 473 | ||
474 | int DataStore::NamedDatabase::scan(const size_t key, | ||
475 | const std::function<bool(size_t key, const QByteArray &value)> &resultHandler, | ||
476 | const std::function<void(const DataStore::Error &error)> &errorHandler, bool skipInternalKeys) const | ||
477 | { | ||
478 | return scan(sizeTToByteArray(key), | ||
479 | [&resultHandler](const QByteArray &key, const QByteArray &value) { | ||
480 | return resultHandler(byteArrayToSizeT(key), value); | ||
481 | }, | ||
482 | errorHandler, /* findSubstringKeys = */ false, skipInternalKeys); | ||
483 | } | ||
484 | |||
448 | int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | 485 | 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 | 486 | const std::function<void(const DataStore::Error &error)> &errorHandler, bool findSubstringKeys, bool skipInternalKeys) const |
450 | { | 487 | { |
@@ -471,8 +508,10 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
471 | 508 | ||
472 | int numberOfRetrievedValues = 0; | 509 | int numberOfRetrievedValues = 0; |
473 | 510 | ||
474 | if (k.isEmpty() || d->allowDuplicates || findSubstringKeys) { | 511 | bool allowDuplicates = d->flags & AllowDuplicates; |
475 | MDB_cursor_op op = d->allowDuplicates ? MDB_SET : MDB_FIRST; | 512 | |
513 | if (k.isEmpty() || allowDuplicates || findSubstringKeys) { | ||
514 | MDB_cursor_op op = allowDuplicates ? MDB_SET : MDB_FIRST; | ||
476 | if (findSubstringKeys) { | 515 | if (findSubstringKeys) { |
477 | op = MDB_SET_RANGE; | 516 | op = MDB_SET_RANGE; |
478 | } | 517 | } |
@@ -490,7 +529,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
490 | key.mv_data = (void *)k.constData(); | 529 | key.mv_data = (void *)k.constData(); |
491 | key.mv_size = k.size(); | 530 | key.mv_size = k.size(); |
492 | } | 531 | } |
493 | MDB_cursor_op nextOp = (d->allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT; | 532 | MDB_cursor_op nextOp = (allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT; |
494 | while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) { | 533 | while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) { |
495 | const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size); | 534 | const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size); |
496 | // Every consequitive lookup simply iterates through the list | 535 | // Every consequitive lookup simply iterates through the list |
@@ -529,6 +568,18 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
529 | return numberOfRetrievedValues; | 568 | return numberOfRetrievedValues; |
530 | } | 569 | } |
531 | 570 | ||
571 | |||
572 | void DataStore::NamedDatabase::findLatest(size_t key, | ||
573 | const std::function<void(size_t key, const QByteArray &value)> &resultHandler, | ||
574 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | ||
575 | { | ||
576 | return findLatest(sizeTToByteArray(key), | ||
577 | [&resultHandler](const QByteArray &key, const QByteArray &value) { | ||
578 | resultHandler(byteArrayToSizeT(value), value); | ||
579 | }, | ||
580 | errorHandler); | ||
581 | } | ||
582 | |||
532 | void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | 583 | void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, |
533 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | 584 | const std::function<void(const DataStore::Error &error)> &errorHandler) const |
534 | { | 585 | { |
@@ -602,6 +653,17 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi | |||
602 | return; | 653 | return; |
603 | } | 654 | } |
604 | 655 | ||
656 | int DataStore::NamedDatabase::findAllInRange(const size_t lowerBound, const size_t upperBound, | ||
657 | const std::function<void(size_t key, const QByteArray &value)> &resultHandler, | ||
658 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | ||
659 | { | ||
660 | return findAllInRange(sizeTToByteArray(lowerBound), sizeTToByteArray(upperBound), | ||
661 | [&resultHandler](const QByteArray &key, const QByteArray &value) { | ||
662 | resultHandler(byteArrayToSizeT(value), value); | ||
663 | }, | ||
664 | errorHandler); | ||
665 | } | ||
666 | |||
605 | int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, | 667 | int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, |
606 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | 668 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, |
607 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | 669 | const std::function<void(const DataStore::Error &error)> &errorHandler) const |
@@ -839,30 +901,8 @@ void DataStore::Transaction::abort() | |||
839 | d->transaction = nullptr; | 901 | d->transaction = nullptr; |
840 | } | 902 | } |
841 | 903 | ||
842 | //Ensure that we opened the correct database by comparing the expected identifier with the one | 904 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, |
843 | //we write to the database on first open. | 905 | const std::function<void(const DataStore::Error &error)> &errorHandler, int flags) const |
844 | static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray &db, bool readOnly) | ||
845 | { | ||
846 | bool openedTheWrongDatabase = false; | ||
847 | auto count = database.scan("__internal_dbname", [db, &openedTheWrongDatabase](const QByteArray &key, const QByteArray &value) ->bool { | ||
848 | if (value != db) { | ||
849 | SinkWarning() << "Opened the wrong database, got " << value << " instead of " << db; | ||
850 | openedTheWrongDatabase = true; | ||
851 | } | ||
852 | return false; | ||
853 | }, | ||
854 | [&](const DataStore::Error &) { | ||
855 | }, false); | ||
856 | //This is the first time we open this database in a write transaction, write the db name | ||
857 | if (!count) { | ||
858 | if (!readOnly) { | ||
859 | database.write("__internal_dbname", db); | ||
860 | } | ||
861 | } | ||
862 | return !openedTheWrongDatabase; | ||
863 | } | ||
864 | |||
865 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const | ||
866 | { | 906 | { |
867 | if (!d) { | 907 | if (!d) { |
868 | SinkError() << "Tried to open database on invalid transaction: " << db; | 908 | SinkError() << "Tried to open database on invalid transaction: " << db; |
@@ -871,7 +911,8 @@ DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray & | |||
871 | Q_ASSERT(d->transaction); | 911 | Q_ASSERT(d->transaction); |
872 | // We don't now if anything changed | 912 | // We don't now if anything changed |
873 | d->implicitCommit = true; | 913 | d->implicitCommit = true; |
874 | auto p = new DataStore::NamedDatabase::Private(db, allowDuplicates, d->defaultErrorHandler, d->name, d->transaction); | 914 | auto p = new DataStore::NamedDatabase::Private( |
915 | db, flags, d->defaultErrorHandler, d->name, d->transaction); | ||
875 | auto ret = p->openDatabase(d->requestedRead, errorHandler); | 916 | auto ret = p->openDatabase(d->requestedRead, errorHandler); |
876 | if (!ret) { | 917 | if (!ret) { |
877 | delete p; | 918 | delete p; |
@@ -883,11 +924,6 @@ DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray & | |||
883 | } | 924 | } |
884 | 925 | ||
885 | auto database = DataStore::NamedDatabase(p); | 926 | auto database = DataStore::NamedDatabase(p); |
886 | if (!ensureCorrectDb(database, db, d->requestedRead)) { | ||
887 | SinkWarning() << "Failed to open the database correctly" << db; | ||
888 | Q_ASSERT(false); | ||
889 | return DataStore::NamedDatabase(); | ||
890 | } | ||
891 | return database; | 927 | return database; |
892 | } | 928 | } |
893 | 929 | ||
@@ -1049,11 +1085,11 @@ public: | |||
1049 | 1085 | ||
1050 | //Create dbis from the given layout. | 1086 | //Create dbis from the given layout. |
1051 | for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { | 1087 | for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { |
1052 | const bool allowDuplicates = it.value(); | 1088 | const int flags = it.value(); |
1053 | MDB_dbi dbi = 0; | 1089 | MDB_dbi dbi = 0; |
1054 | const auto db = it.key(); | 1090 | const auto db = it.key(); |
1055 | const auto dbiName = name + db; | 1091 | const auto dbiName = name + db; |
1056 | if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { | 1092 | if (createDbi(transaction, db, readOnly, flags, dbi)) { |
1057 | sDbis.insert(dbiName, dbi); | 1093 | sDbis.insert(dbiName, dbi); |
1058 | } | 1094 | } |
1059 | } | 1095 | } |
@@ -1063,8 +1099,8 @@ public: | |||
1063 | MDB_dbi dbi = 0; | 1099 | MDB_dbi dbi = 0; |
1064 | const auto dbiName = name + db; | 1100 | const auto dbiName = name + db; |
1065 | //We're going to load the flags anyways. | 1101 | //We're going to load the flags anyways. |
1066 | bool allowDuplicates = false; | 1102 | const int flags = 0; |
1067 | if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { | 1103 | if (createDbi(transaction, db, readOnly, flags, dbi)) { |
1068 | sDbis.insert(dbiName, dbi); | 1104 | sDbis.insert(dbiName, dbi); |
1069 | } | 1105 | } |
1070 | } | 1106 | } |
diff --git a/common/utils.cpp b/common/utils.cpp index 3c54db4..f6c6798 100644 --- a/common/utils.cpp +++ b/common/utils.cpp | |||
@@ -23,3 +23,13 @@ QByteArray Sink::createUuid() | |||
23 | { | 23 | { |
24 | return QUuid::createUuid().toByteArray(); | 24 | return QUuid::createUuid().toByteArray(); |
25 | } | 25 | } |
26 | |||
27 | const QByteArray Sink::sizeTToByteArray(const size_t &value) | ||
28 | { | ||
29 | return QByteArray::fromRawData(reinterpret_cast<const char *>(&value), sizeof(size_t)); | ||
30 | } | ||
31 | |||
32 | size_t Sink::byteArrayToSizeT(const QByteArray &value) | ||
33 | { | ||
34 | return *reinterpret_cast<const size_t *>(value.constData()); | ||
35 | } | ||
diff --git a/common/utils.h b/common/utils.h index 7066d79..8565f17 100644 --- a/common/utils.h +++ b/common/utils.h | |||
@@ -26,6 +26,20 @@ namespace Sink { | |||
26 | 26 | ||
27 | QByteArray createUuid(); | 27 | QByteArray createUuid(); |
28 | 28 | ||
29 | // No copy is done on this functions. Therefore, the caller must not use the | ||
30 | // returned QByteArray after the size_t has been destroyed. | ||
31 | const QByteArray sizeTToByteArray(const size_t &); | ||
32 | size_t byteArrayToSizeT(const QByteArray &); | ||
33 | |||
34 | template <typename T> | ||
35 | static QByteArray padNumber(T number); | ||
36 | |||
37 | template <> | ||
38 | QByteArray padNumber<size_t>(size_t number) | ||
39 | { | ||
40 | return padNumber<qint64>(number); | ||
41 | } | ||
42 | |||
29 | template <typename T> | 43 | template <typename T> |
30 | static QByteArray padNumber(T number) | 44 | static QByteArray padNumber(T number) |
31 | { | 45 | { |
diff --git a/sinksh/syntax_modules/sink_inspect.cpp b/sinksh/syntax_modules/sink_inspect.cpp index 1d2d90f..52a9e0f 100644 --- a/sinksh/syntax_modules/sink_inspect.cpp +++ b/sinksh/syntax_modules/sink_inspect.cpp | |||
@@ -87,13 +87,13 @@ bool inspect(const QStringList &args, State &state) | |||
87 | [&] (const Sink::Storage::DataStore::Error &e) { | 87 | [&] (const Sink::Storage::DataStore::Error &e) { |
88 | Q_ASSERT(false); | 88 | Q_ASSERT(false); |
89 | state.printError(e.message); | 89 | state.printError(e.message); |
90 | }, false); | 90 | }, Sink::Storage::IntegerKeys); |
91 | 91 | ||
92 | auto ridMap = syncTransaction.openDatabase("localid.mapping." + type, | 92 | auto ridMap = syncTransaction.openDatabase("localid.mapping." + type, |
93 | [&] (const Sink::Storage::DataStore::Error &e) { | 93 | [&] (const Sink::Storage::DataStore::Error &e) { |
94 | Q_ASSERT(false); | 94 | Q_ASSERT(false); |
95 | state.printError(e.message); | 95 | state.printError(e.message); |
96 | }, false); | 96 | }); |
97 | 97 | ||
98 | QHash<QByteArray, QByteArray> hash; | 98 | QHash<QByteArray, QByteArray> hash; |
99 | 99 | ||
@@ -108,7 +108,8 @@ bool inspect(const QStringList &args, State &state) | |||
108 | 108 | ||
109 | QSet<QByteArray> uids; | 109 | QSet<QByteArray> uids; |
110 | db.scan("", [&] (const QByteArray &key, const QByteArray &data) { | 110 | db.scan("", [&] (const QByteArray &key, const QByteArray &data) { |
111 | uids.insert(Key::fromInternalByteArray(key).identifier().toDisplayByteArray()); | 111 | size_t revision = Sink::byteArrayToSizeT(key); |
112 | uids.insert(Sink::Storage::DataStore::getUidFromRevision(transaction, revision)); | ||
112 | return true; | 113 | return true; |
113 | }, | 114 | }, |
114 | [&](const Sink::Storage::DataStore::Error &e) { | 115 | [&](const Sink::Storage::DataStore::Error &e) { |
@@ -180,7 +181,7 @@ bool inspect(const QStringList &args, State &state) | |||
180 | [&] (const Sink::Storage::DataStore::Error &e) { | 181 | [&] (const Sink::Storage::DataStore::Error &e) { |
181 | Q_ASSERT(false); | 182 | Q_ASSERT(false); |
182 | state.printError(e.message); | 183 | state.printError(e.message); |
183 | }, false); | 184 | }); |
184 | 185 | ||
185 | if (showInternal) { | 186 | if (showInternal) { |
186 | //Print internal keys | 187 | //Print internal keys |
diff --git a/tests/dbwriter.cpp b/tests/dbwriter.cpp index 3045eac..a25faec 100644 --- a/tests/dbwriter.cpp +++ b/tests/dbwriter.cpp | |||
@@ -29,14 +29,14 @@ int main(int argc, char *argv[]) | |||
29 | qWarning() << "No valid transaction"; | 29 | qWarning() << "No valid transaction"; |
30 | return -1; | 30 | return -1; |
31 | } | 31 | } |
32 | transaction.openDatabase("a", nullptr, false).write(QByteArray::number(i), "a"); | 32 | transaction.openDatabase("a", nullptr, 0).write(QByteArray::number(i), "a"); |
33 | transaction.openDatabase("b", nullptr, false).write(QByteArray::number(i), "b"); | 33 | transaction.openDatabase("b", nullptr, 0).write(QByteArray::number(i), "b"); |
34 | transaction.openDatabase("c", nullptr, false).write(QByteArray::number(i), "c"); | 34 | transaction.openDatabase("c", nullptr, 0).write(QByteArray::number(i), "c"); |
35 | transaction.openDatabase("p", nullptr, false).write(QByteArray::number(i), "c"); | 35 | transaction.openDatabase("p", nullptr, 0).write(QByteArray::number(i), "c"); |
36 | transaction.openDatabase("q", nullptr, false).write(QByteArray::number(i), "c"); | 36 | transaction.openDatabase("q", nullptr, 0).write(QByteArray::number(i), "c"); |
37 | if (i > (count/2)) { | 37 | if (i > (count/2)) { |
38 | for (int d = 0; d < 40; d++) { | 38 | for (int d = 0; d < 40; d++) { |
39 | transaction.openDatabase("db" + QByteArray::number(d), nullptr, false).write(QByteArray::number(i), "a"); | 39 | transaction.openDatabase("db" + QByteArray::number(d), nullptr, 0).write(QByteArray::number(i), "a"); |
40 | } | 40 | } |
41 | } | 41 | } |
42 | if ((i % 1000) == 0) { | 42 | if ((i % 1000) == 0) { |
diff --git a/tests/entitystoretest.cpp b/tests/entitystoretest.cpp index 03b940b..608de4c 100644 --- a/tests/entitystoretest.cpp +++ b/tests/entitystoretest.cpp | |||
@@ -18,6 +18,7 @@ private slots: | |||
18 | void initTestCase() | 18 | void initTestCase() |
19 | { | 19 | { |
20 | Sink::AdaptorFactoryRegistry::instance().registerFactory<Sink::ApplicationDomain::Mail, TestMailAdaptorFactory>("test"); | 20 | Sink::AdaptorFactoryRegistry::instance().registerFactory<Sink::ApplicationDomain::Mail, TestMailAdaptorFactory>("test"); |
21 | Sink::AdaptorFactoryRegistry::instance().registerFactory<Sink::ApplicationDomain::Event, TestEventAdaptorFactory>("test"); | ||
21 | } | 22 | } |
22 | 23 | ||
23 | void cleanup() | 24 | void cleanup() |
@@ -29,6 +30,100 @@ private slots: | |||
29 | { | 30 | { |
30 | } | 31 | } |
31 | 32 | ||
33 | void testFullScan() | ||
34 | { | ||
35 | using namespace Sink; | ||
36 | ResourceContext resourceContext{resourceInstanceIdentifier.toUtf8(), "dummy", AdaptorFactoryRegistry::instance().getFactories("test")}; | ||
37 | Storage::EntityStore store(resourceContext, {}); | ||
38 | |||
39 | auto mail = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
40 | mail.setExtractedMessageId("messageid"); | ||
41 | mail.setExtractedSubject("boo"); | ||
42 | |||
43 | auto mail2 = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
44 | mail2.setExtractedMessageId("messageid2"); | ||
45 | mail2.setExtractedSubject("foo"); | ||
46 | |||
47 | auto mail3 = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
48 | mail3.setExtractedMessageId("messageid2"); | ||
49 | mail3.setExtractedSubject("foo"); | ||
50 | |||
51 | store.startTransaction(Storage::DataStore::ReadWrite); | ||
52 | store.add("mail", mail, false); | ||
53 | store.add("mail", mail2, false); | ||
54 | store.add("mail", mail3, false); | ||
55 | |||
56 | mail.setExtractedSubject("foo"); | ||
57 | |||
58 | store.modify("mail", mail, QByteArrayList{}, false); | ||
59 | |||
60 | { | ||
61 | const auto ids = store.fullScan("mail"); | ||
62 | |||
63 | QCOMPARE(ids.size(), 3); | ||
64 | QVERIFY(ids.contains(Sink::Storage::Identifier::fromDisplayByteArray(mail.identifier()))); | ||
65 | QVERIFY(ids.contains(Sink::Storage::Identifier::fromDisplayByteArray(mail2.identifier()))); | ||
66 | QVERIFY(ids.contains(Sink::Storage::Identifier::fromDisplayByteArray(mail3.identifier()))); | ||
67 | } | ||
68 | |||
69 | store.remove("mail", mail3, false); | ||
70 | store.commitTransaction(); | ||
71 | |||
72 | { | ||
73 | const auto ids = store.fullScan("mail"); | ||
74 | |||
75 | QCOMPARE(ids.size(), 2); | ||
76 | QVERIFY(ids.contains(Sink::Storage::Identifier::fromDisplayByteArray(mail.identifier()))); | ||
77 | QVERIFY(ids.contains(Sink::Storage::Identifier::fromDisplayByteArray(mail2.identifier()))); | ||
78 | } | ||
79 | } | ||
80 | |||
81 | void testExistsAndContains() | ||
82 | { | ||
83 | |||
84 | using namespace Sink; | ||
85 | ResourceContext resourceContext{resourceInstanceIdentifier.toUtf8(), "dummy", AdaptorFactoryRegistry::instance().getFactories("test")}; | ||
86 | Storage::EntityStore store(resourceContext, {}); | ||
87 | |||
88 | auto mail = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
89 | mail.setExtractedMessageId("messageid"); | ||
90 | mail.setExtractedSubject("boo"); | ||
91 | |||
92 | auto mail2 = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
93 | mail2.setExtractedMessageId("messageid2"); | ||
94 | mail2.setExtractedSubject("foo"); | ||
95 | |||
96 | auto mail3 = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Mail>("res1"); | ||
97 | mail3.setExtractedMessageId("messageid2"); | ||
98 | mail3.setExtractedSubject("foo"); | ||
99 | |||
100 | auto event = ApplicationDomain::ApplicationDomainType::createEntity<ApplicationDomain::Event>("res1"); | ||
101 | event.setExtractedUid("messageid2"); | ||
102 | event.setExtractedSummary("foo"); | ||
103 | |||
104 | store.startTransaction(Storage::DataStore::ReadWrite); | ||
105 | store.add("mail", mail, false); | ||
106 | store.add("mail", mail2, false); | ||
107 | store.add("mail", mail3, false); | ||
108 | store.add("event", event, false); | ||
109 | |||
110 | mail.setExtractedSubject("foo"); | ||
111 | |||
112 | store.modify("mail", mail, QByteArrayList{}, false); | ||
113 | store.remove("mail", mail3, false); | ||
114 | store.commitTransaction(); | ||
115 | |||
116 | QVERIFY(store.contains("mail", mail.identifier())); | ||
117 | QVERIFY(store.contains("mail", mail2.identifier())); | ||
118 | QVERIFY(store.contains("mail", mail3.identifier())); | ||
119 | QVERIFY(store.contains("event", event.identifier())); | ||
120 | |||
121 | QVERIFY(store.exists("mail", mail.identifier())); | ||
122 | QVERIFY(store.exists("mail", mail2.identifier())); | ||
123 | QVERIFY(!store.exists("mail", mail3.identifier())); | ||
124 | QVERIFY(store.exists("event", event.identifier())); | ||
125 | } | ||
126 | |||
32 | void readAll() | 127 | void readAll() |
33 | { | 128 | { |
34 | using namespace Sink; | 129 | using namespace Sink; |
diff --git a/tests/pipelinetest.cpp b/tests/pipelinetest.cpp index b41a5c2..801a9e0 100644 --- a/tests/pipelinetest.cpp +++ b/tests/pipelinetest.cpp | |||
@@ -28,26 +28,29 @@ static void removeFromDisk(const QString &name) | |||
28 | store.removeFromDisk(); | 28 | store.removeFromDisk(); |
29 | } | 29 | } |
30 | 30 | ||
31 | static QList<QByteArray> getKeys(const QByteArray &dbEnv, const QByteArray &name) | 31 | static QList<Sink::Storage::Key> getKeys(const QByteArray &dbEnv, const QByteArray &name) |
32 | { | 32 | { |
33 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); | 33 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); |
34 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 34 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
35 | auto db = transaction.openDatabase(name, nullptr, false); | 35 | auto db = transaction.openDatabase(name, nullptr, Sink::Storage::IntegerKeys); |
36 | QList<QByteArray> result; | 36 | QList<Sink::Storage::Key> result; |
37 | db.scan("", [&](const QByteArray &key, const QByteArray &value) { | 37 | db.scan("", [&](const QByteArray &key, const QByteArray &value) { |
38 | result << key; | 38 | size_t revision = Sink::byteArrayToSizeT(key); |
39 | result << Sink::Storage::Key(Sink::Storage::Identifier::fromDisplayByteArray( | ||
40 | Sink::Storage::DataStore::getUidFromRevision(transaction, revision)), | ||
41 | revision); | ||
39 | return true; | 42 | return true; |
40 | }); | 43 | }); |
41 | return result; | 44 | return result; |
42 | } | 45 | } |
43 | 46 | ||
44 | static QByteArray getEntity(const QByteArray &dbEnv, const QByteArray &name, const QByteArray &uid) | 47 | static QByteArray getEntity(const QByteArray &dbEnv, const QByteArray &name, const Sink::Storage::Key &key) |
45 | { | 48 | { |
46 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); | 49 | Sink::Storage::DataStore store(Sink::storageLocation(), dbEnv, Sink::Storage::DataStore::ReadOnly); |
47 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 50 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
48 | auto db = transaction.openDatabase(name, nullptr, false); | 51 | auto db = transaction.openDatabase(name, nullptr, Sink::Storage::IntegerKeys); |
49 | QByteArray result; | 52 | QByteArray result; |
50 | db.scan(uid, [&](const QByteArray &key, const QByteArray &value) { | 53 | db.scan(key.revision().toSizeT(), [&](size_t rev, const QByteArray &value) { |
51 | result = value; | 54 | result = value; |
52 | return true; | 55 | return true; |
53 | }); | 56 | }); |
@@ -251,7 +254,7 @@ private slots: | |||
251 | // Get uid of written entity | 254 | // Get uid of written entity |
252 | auto keys = getKeys(instanceIdentifier(), "event.main"); | 255 | auto keys = getKeys(instanceIdentifier(), "event.main"); |
253 | QCOMPARE(keys.size(), 1); | 256 | QCOMPARE(keys.size(), 1); |
254 | auto key = Sink::Storage::Key::fromInternalByteArray(keys.first()); | 257 | auto key = keys.first(); |
255 | const auto uid = key.identifier().toDisplayByteArray(); | 258 | const auto uid = key.identifier().toDisplayByteArray(); |
256 | 259 | ||
257 | // Execute the modification | 260 | // Execute the modification |
@@ -264,7 +267,7 @@ private slots: | |||
264 | key.setRevision(2); | 267 | key.setRevision(2); |
265 | 268 | ||
266 | // Ensure we've got the new revision with the modification | 269 | // Ensure we've got the new revision with the modification |
267 | auto buffer = getEntity(instanceIdentifier(), "event.main", key.toInternalByteArray()); | 270 | auto buffer = getEntity(instanceIdentifier(), "event.main", key); |
268 | QVERIFY(!buffer.isEmpty()); | 271 | QVERIFY(!buffer.isEmpty()); |
269 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); | 272 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); |
270 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); | 273 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); |
@@ -299,7 +302,7 @@ private slots: | |||
299 | // Get uid of written entity | 302 | // Get uid of written entity |
300 | auto keys = getKeys(instanceIdentifier(), "event.main"); | 303 | auto keys = getKeys(instanceIdentifier(), "event.main"); |
301 | QCOMPARE(keys.size(), 1); | 304 | QCOMPARE(keys.size(), 1); |
302 | auto key = Sink::Storage::Key::fromInternalByteArray(keys.first()); | 305 | auto key = keys.first(); |
303 | const auto uid = key.identifier().toDisplayByteArray(); | 306 | const auto uid = key.identifier().toDisplayByteArray(); |
304 | 307 | ||
305 | 308 | ||
@@ -322,7 +325,7 @@ private slots: | |||
322 | key.setRevision(3); | 325 | key.setRevision(3); |
323 | 326 | ||
324 | // Ensure we've got the new revision with the modification | 327 | // Ensure we've got the new revision with the modification |
325 | auto buffer = getEntity(instanceIdentifier(), "event.main", key.toInternalByteArray()); | 328 | auto buffer = getEntity(instanceIdentifier(), "event.main", key); |
326 | QVERIFY(!buffer.isEmpty()); | 329 | QVERIFY(!buffer.isEmpty()); |
327 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); | 330 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); |
328 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); | 331 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); |
@@ -343,7 +346,7 @@ private slots: | |||
343 | auto result = getKeys(instanceIdentifier(), "event.main"); | 346 | auto result = getKeys(instanceIdentifier(), "event.main"); |
344 | QCOMPARE(result.size(), 1); | 347 | QCOMPARE(result.size(), 1); |
345 | 348 | ||
346 | const auto uid = Sink::Storage::Key::fromInternalByteArray(result.first()).identifier().toDisplayByteArray(); | 349 | const auto uid = result.first().identifier().toDisplayByteArray(); |
347 | 350 | ||
348 | // Delete entity | 351 | // Delete entity |
349 | auto deleteCommand = deleteEntityCommand(uid, 1); | 352 | auto deleteCommand = deleteEntityCommand(uid, 1); |
@@ -386,7 +389,7 @@ private slots: | |||
386 | pipeline.startTransaction(); | 389 | pipeline.startTransaction(); |
387 | auto keys = getKeys(instanceIdentifier(), "event.main"); | 390 | auto keys = getKeys(instanceIdentifier(), "event.main"); |
388 | QCOMPARE(keys.size(), 1); | 391 | QCOMPARE(keys.size(), 1); |
389 | const auto uid = Sink::Storage::Key::fromInternalByteArray(keys.first()).identifier().toDisplayByteArray(); | 392 | const auto uid = keys.first().identifier().toDisplayByteArray(); |
390 | { | 393 | { |
391 | auto modifyCommand = modifyEntityCommand(createEvent(entityFbb, "summary2"), uid, 1); | 394 | auto modifyCommand = modifyEntityCommand(createEvent(entityFbb, "summary2"), uid, 1); |
392 | pipeline.modifiedEntity(modifyCommand.constData(), modifyCommand.size()); | 395 | pipeline.modifiedEntity(modifyCommand.constData(), modifyCommand.size()); |
@@ -427,7 +430,7 @@ private slots: | |||
427 | // Get uid of written entity | 430 | // Get uid of written entity |
428 | auto keys = getKeys(instanceIdentifier(), "event.main"); | 431 | auto keys = getKeys(instanceIdentifier(), "event.main"); |
429 | QCOMPARE(keys.size(), 1); | 432 | QCOMPARE(keys.size(), 1); |
430 | auto key = Sink::Storage::Key::fromInternalByteArray(keys.first()); | 433 | auto key = keys.first(); |
431 | const auto uid = key.identifier().toDisplayByteArray(); | 434 | const auto uid = key.identifier().toDisplayByteArray(); |
432 | 435 | ||
433 | //Simulate local modification | 436 | //Simulate local modification |
@@ -453,7 +456,7 @@ private slots: | |||
453 | key.setRevision(3); | 456 | key.setRevision(3); |
454 | 457 | ||
455 | // Ensure we've got the new revision with the modification | 458 | // Ensure we've got the new revision with the modification |
456 | auto buffer = getEntity(instanceIdentifier(), "event.main", key.toInternalByteArray()); | 459 | auto buffer = getEntity(instanceIdentifier(), "event.main", key); |
457 | QVERIFY(!buffer.isEmpty()); | 460 | QVERIFY(!buffer.isEmpty()); |
458 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); | 461 | Sink::EntityBuffer entityBuffer(buffer.data(), buffer.size()); |
459 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); | 462 | auto adaptor = adaptorFactory->createAdaptor(entityBuffer.entity()); |
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp index 81acc13..3d583ab 100644 --- a/tests/storagetest.cpp +++ b/tests/storagetest.cpp | |||
@@ -227,7 +227,7 @@ private slots: | |||
227 | bool gotError = false; | 227 | bool gotError = false; |
228 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0}}}, Sink::Storage::DataStore::ReadWrite); | 228 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0}}}, Sink::Storage::DataStore::ReadWrite); |
229 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 229 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
230 | auto db = transaction.openDatabase("default", nullptr, false); | 230 | auto db = transaction.openDatabase("default"); |
231 | db.write("key", "value"); | 231 | db.write("key", "value"); |
232 | db.write("key", "value"); | 232 | db.write("key", "value"); |
233 | 233 | ||
@@ -250,9 +250,10 @@ private slots: | |||
250 | { | 250 | { |
251 | bool gotResult = false; | 251 | bool gotResult = false; |
252 | bool gotError = false; | 252 | bool gotError = false; |
253 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", 0x04}}}, Sink::Storage::DataStore::ReadWrite); | 253 | const int flags = Sink::Storage::AllowDuplicates; |
254 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"default", flags}}}, Sink::Storage::DataStore::ReadWrite); | ||
254 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 255 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
255 | auto db = transaction.openDatabase("default", nullptr, true); | 256 | auto db = transaction.openDatabase("default", nullptr, flags); |
256 | db.write("key", "value1"); | 257 | db.write("key", "value1"); |
257 | db.write("key", "value2"); | 258 | db.write("key", "value2"); |
258 | int numValues = db.scan("key", | 259 | int numValues = db.scan("key", |
@@ -357,7 +358,7 @@ private slots: | |||
357 | 358 | ||
358 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 359 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
359 | store.createTransaction(Sink::Storage::DataStore::ReadWrite) | 360 | store.createTransaction(Sink::Storage::DataStore::ReadWrite) |
360 | .openDatabase("test", nullptr, true) | 361 | .openDatabase("test", nullptr, Sink::Storage::AllowDuplicates) |
361 | .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) { | 362 | .write("key1", "value1", [&](const Sink::Storage::DataStore::Error &error) { |
362 | qDebug() << error.message; | 363 | qDebug() << error.message; |
363 | gotError = true; | 364 | gotError = true; |
@@ -368,9 +369,10 @@ private slots: | |||
368 | // By default we want only exact matches | 369 | // By default we want only exact matches |
369 | void testSubstringKeys() | 370 | void testSubstringKeys() |
370 | { | 371 | { |
371 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0x04}}}, Sink::Storage::DataStore::ReadWrite); | 372 | const int flags = Sink::Storage::AllowDuplicates; |
373 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", flags}}}, Sink::Storage::DataStore::ReadWrite); | ||
372 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 374 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
373 | auto db = transaction.openDatabase("test", nullptr, true); | 375 | auto db = transaction.openDatabase("test", nullptr, flags); |
374 | db.write("sub", "value1"); | 376 | db.write("sub", "value1"); |
375 | db.write("subsub", "value2"); | 377 | db.write("subsub", "value2"); |
376 | int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool { return true; }); | 378 | int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool { return true; }); |
@@ -382,7 +384,7 @@ private slots: | |||
382 | { | 384 | { |
383 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 385 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
384 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 386 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
385 | auto db = transaction.openDatabase("test", nullptr, false); | 387 | auto db = transaction.openDatabase("test"); |
386 | db.write("sub", "value1"); | 388 | db.write("sub", "value1"); |
387 | db.write("subsub", "value2"); | 389 | db.write("subsub", "value2"); |
388 | db.write("wubsub", "value3"); | 390 | db.write("wubsub", "value3"); |
@@ -395,7 +397,7 @@ private slots: | |||
395 | { | 397 | { |
396 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 398 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
397 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 399 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
398 | auto db = transaction.openDatabase("test", nullptr, true); | 400 | auto db = transaction.openDatabase("test", nullptr, Sink::Storage::AllowDuplicates); |
399 | db.write("sub", "value1"); | 401 | db.write("sub", "value1"); |
400 | db.write("subsub", "value2"); | 402 | db.write("subsub", "value2"); |
401 | db.write("wubsub", "value3"); | 403 | db.write("wubsub", "value3"); |
@@ -408,7 +410,7 @@ private slots: | |||
408 | { | 410 | { |
409 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 411 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
410 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 412 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
411 | auto db = transaction.openDatabase("test", nullptr, false); | 413 | auto db = transaction.openDatabase("test"); |
412 | db.write("sub_2", "value2"); | 414 | db.write("sub_2", "value2"); |
413 | db.write("sub_1", "value1"); | 415 | db.write("sub_1", "value1"); |
414 | db.write("sub_3", "value3"); | 416 | db.write("sub_3", "value3"); |
@@ -429,7 +431,7 @@ private slots: | |||
429 | { | 431 | { |
430 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 432 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
431 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 433 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
432 | auto db = transaction.openDatabase("test", nullptr, true); | 434 | auto db = transaction.openDatabase("test", nullptr, Sink::Storage::AllowDuplicates); |
433 | db.write("sub1", "value1"); | 435 | db.write("sub1", "value1"); |
434 | int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool { return true; }); | 436 | int numValues = db.scan("sub", [&](const QByteArray &key, const QByteArray &value) -> bool { return true; }); |
435 | 437 | ||
@@ -440,7 +442,7 @@ private slots: | |||
440 | { | 442 | { |
441 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 443 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
442 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 444 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
443 | auto db = transaction.openDatabase("test", nullptr, false); | 445 | auto db = transaction.openDatabase("test"); |
444 | db.write("sub1", "value1"); | 446 | db.write("sub1", "value1"); |
445 | db.write("sub2", "value2"); | 447 | db.write("sub2", "value2"); |
446 | db.write("wub3", "value3"); | 448 | db.write("wub3", "value3"); |
@@ -455,7 +457,7 @@ private slots: | |||
455 | { | 457 | { |
456 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 458 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
457 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 459 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
458 | auto db = transaction.openDatabase("test", nullptr, false); | 460 | auto db = transaction.openDatabase("test"); |
459 | db.write("sub2", "value2"); | 461 | db.write("sub2", "value2"); |
460 | QByteArray result; | 462 | QByteArray result; |
461 | db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { result = value; }); | 463 | db.findLatest("sub", [&](const QByteArray &key, const QByteArray &value) { result = value; }); |
@@ -467,7 +469,7 @@ private slots: | |||
467 | { | 469 | { |
468 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 470 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
469 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 471 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
470 | auto db = transaction.openDatabase("test", nullptr, false); | 472 | auto db = transaction.openDatabase("test"); |
471 | db.write("sub2", "value2"); | 473 | db.write("sub2", "value2"); |
472 | db.write("wub3", "value3"); | 474 | db.write("wub3", "value3"); |
473 | QByteArray result; | 475 | QByteArray result; |
@@ -478,8 +480,8 @@ private slots: | |||
478 | 480 | ||
479 | static QMap<QByteArray, int> baseDbs() | 481 | static QMap<QByteArray, int> baseDbs() |
480 | { | 482 | { |
481 | return {{"revisionType", 0}, | 483 | return {{"revisionType", Sink::Storage::IntegerKeys}, |
482 | {"revisions", 0}, | 484 | {"revisions", Sink::Storage::IntegerKeys}, |
483 | {"uids", 0}, | 485 | {"uids", 0}, |
484 | {"default", 0}, | 486 | {"default", 0}, |
485 | {"__flagtable", 0}}; | 487 | {"__flagtable", 0}}; |
@@ -499,7 +501,7 @@ private slots: | |||
499 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); | 501 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"test", 0}}}, Sink::Storage::DataStore::ReadWrite); |
500 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 502 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
501 | QByteArray result; | 503 | QByteArray result; |
502 | auto db = transaction.openDatabase("test", nullptr, false); | 504 | auto db = transaction.openDatabase("test"); |
503 | const auto uid = "{c5d06a9f-1534-4c52-b8ea-415db68bdadf}"; | 505 | const auto uid = "{c5d06a9f-1534-4c52-b8ea-415db68bdadf}"; |
504 | //Ensure we can sort 1 and 10 properly (by default string comparison 10 comes before 6) | 506 | //Ensure we can sort 1 and 10 properly (by default string comparison 10 comes before 6) |
505 | const auto id = Sink::Storage::Identifier::fromDisplayByteArray(uid); | 507 | const auto id = Sink::Storage::Identifier::fromDisplayByteArray(uid); |
@@ -523,7 +525,7 @@ private slots: | |||
523 | { | 525 | { |
524 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | 526 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); |
525 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 527 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
526 | auto db = transaction.openDatabase("test", nullptr, false); | 528 | auto db = transaction.openDatabase("test"); |
527 | setupTestFindRange(db); | 529 | setupTestFindRange(db); |
528 | QByteArrayList results; | 530 | QByteArrayList results; |
529 | db.findAllInRange("0002", "0004", [&](const QByteArray &key, const QByteArray &value) { results << value; }); | 531 | db.findAllInRange("0002", "0004", [&](const QByteArray &key, const QByteArray &value) { results << value; }); |
@@ -535,7 +537,7 @@ private slots: | |||
535 | { | 537 | { |
536 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | 538 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); |
537 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 539 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
538 | auto db = transaction.openDatabase("test", nullptr, false); | 540 | auto db = transaction.openDatabase("test"); |
539 | setupTestFindRange(db); | 541 | setupTestFindRange(db); |
540 | 542 | ||
541 | QByteArrayList results1; | 543 | QByteArrayList results1; |
@@ -559,7 +561,7 @@ private slots: | |||
559 | { | 561 | { |
560 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | 562 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); |
561 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 563 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
562 | auto db = transaction.openDatabase("test", nullptr, false); | 564 | auto db = transaction.openDatabase("test"); |
563 | setupTestFindRange(db); | 565 | setupTestFindRange(db); |
564 | 566 | ||
565 | QByteArrayList results1; | 567 | QByteArrayList results1; |
@@ -571,7 +573,7 @@ private slots: | |||
571 | { | 573 | { |
572 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); | 574 | Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadWrite); |
573 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 575 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
574 | auto db = transaction.openDatabase("test", nullptr, false); | 576 | auto db = transaction.openDatabase("test"); |
575 | setupTestFindRange(db); | 577 | setupTestFindRange(db); |
576 | 578 | ||
577 | QByteArrayList results1; | 579 | QByteArrayList results1; |
@@ -601,21 +603,21 @@ private slots: | |||
601 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite); | 603 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite); |
602 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 604 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
603 | 605 | ||
604 | auto db = transaction.openDatabase("testTransactionVisibility", nullptr, false); | 606 | auto db = transaction.openDatabase("testTransactionVisibility"); |
605 | db.write("key1", "foo"); | 607 | db.write("key1", "foo"); |
606 | QCOMPARE(readValue(db, "key1"), QByteArray("foo")); | 608 | QCOMPARE(readValue(db, "key1"), QByteArray("foo")); |
607 | 609 | ||
608 | { | 610 | { |
609 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 611 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
610 | auto db2 = transaction2 | 612 | auto db2 = transaction2 |
611 | .openDatabase("testTransactionVisibility", nullptr, false); | 613 | .openDatabase("testTransactionVisibility"); |
612 | QCOMPARE(readValue(db2, "key1"), QByteArray()); | 614 | QCOMPARE(readValue(db2, "key1"), QByteArray()); |
613 | } | 615 | } |
614 | transaction.commit(); | 616 | transaction.commit(); |
615 | { | 617 | { |
616 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 618 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
617 | auto db2 = transaction2 | 619 | auto db2 = transaction2 |
618 | .openDatabase("testTransactionVisibility", nullptr, false); | 620 | .openDatabase("testTransactionVisibility"); |
619 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); | 621 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); |
620 | } | 622 | } |
621 | 623 | ||
@@ -627,16 +629,16 @@ private slots: | |||
627 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"a", 0}, {"b", 0}, {"c", 0}}}, Sink::Storage::DataStore::ReadWrite); | 629 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"a", 0}, {"b", 0}, {"c", 0}}}, Sink::Storage::DataStore::ReadWrite); |
628 | { | 630 | { |
629 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 631 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
630 | transaction.openDatabase("a", nullptr, false); | 632 | transaction.openDatabase("a"); |
631 | transaction.openDatabase("b", nullptr, false); | 633 | transaction.openDatabase("b"); |
632 | transaction.openDatabase("c", nullptr, false); | 634 | transaction.openDatabase("c"); |
633 | transaction.commit(); | 635 | transaction.commit(); |
634 | } | 636 | } |
635 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 637 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
636 | for (int i = 0; i < 1000; i++) { | 638 | for (int i = 0; i < 1000; i++) { |
637 | transaction.openDatabase("a", nullptr, false); | 639 | transaction.openDatabase("a"); |
638 | transaction.openDatabase("b", nullptr, false); | 640 | transaction.openDatabase("b"); |
639 | transaction.openDatabase("c", nullptr, false); | 641 | transaction.openDatabase("c"); |
640 | transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 642 | transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
641 | } | 643 | } |
642 | } | 644 | } |
@@ -662,11 +664,11 @@ private slots: | |||
662 | // Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadOnly); | 664 | // Sink::Storage::DataStore store(testDataPath, dbName, Sink::Storage::DataStore::ReadOnly); |
663 | // auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 665 | // auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
664 | // for (int i = 0; i < 100000; i++) { | 666 | // for (int i = 0; i < 100000; i++) { |
665 | // transaction.openDatabase("a", nullptr, false); | 667 | // transaction.openDatabase("a"); |
666 | // transaction.openDatabase("b", nullptr, false); | 668 | // transaction.openDatabase("b"); |
667 | // transaction.openDatabase("c", nullptr, false); | 669 | // transaction.openDatabase("c"); |
668 | // transaction.openDatabase("p", nullptr, false); | 670 | // transaction.openDatabase("p"); |
669 | // transaction.openDatabase("q", nullptr, false); | 671 | // transaction.openDatabase("q"); |
670 | // } | 672 | // } |
671 | // }); | 673 | // }); |
672 | // } | 674 | // } |
@@ -733,7 +735,7 @@ private slots: | |||
733 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite); | 735 | Sink::Storage::DataStore store(testDataPath, {dbName, {{"testTransactionVisibility", 0}}}, Sink::Storage::DataStore::ReadWrite); |
734 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 736 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
735 | 737 | ||
736 | auto db = transaction.openDatabase("testTransactionVisibility", nullptr, false); | 738 | auto db = transaction.openDatabase("testTransactionVisibility"); |
737 | db.write("key1", "foo"); | 739 | db.write("key1", "foo"); |
738 | QCOMPARE(readValue(db, "key1"), QByteArray("foo")); | 740 | QCOMPARE(readValue(db, "key1"), QByteArray("foo")); |
739 | transaction.commit(); | 741 | transaction.commit(); |
@@ -748,12 +750,12 @@ private slots: | |||
748 | 750 | ||
749 | //This transaction should open the dbi | 751 | //This transaction should open the dbi |
750 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 752 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
751 | auto db2 = transaction2.openDatabase("testTransactionVisibility", nullptr, false); | 753 | auto db2 = transaction2.openDatabase("testTransactionVisibility"); |
752 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); | 754 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); |
753 | 755 | ||
754 | //This transaction should have the dbi available | 756 | //This transaction should have the dbi available |
755 | auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 757 | auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
756 | auto db3 = transaction3.openDatabase("testTransactionVisibility", nullptr, false); | 758 | auto db3 = transaction3.openDatabase("testTransactionVisibility"); |
757 | QCOMPARE(readValue(db3, "key1"), QByteArray("foo")); | 759 | QCOMPARE(readValue(db3, "key1"), QByteArray("foo")); |
758 | } | 760 | } |
759 | 761 | ||
@@ -766,20 +768,198 @@ private slots: | |||
766 | 768 | ||
767 | //This transaction should open the dbi | 769 | //This transaction should open the dbi |
768 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | 770 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadWrite); |
769 | auto db2 = transaction2.openDatabase("testTransactionVisibility", nullptr, false); | 771 | auto db2 = transaction2.openDatabase("testTransactionVisibility"); |
770 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); | 772 | QCOMPARE(readValue(db2, "key1"), QByteArray("foo")); |
771 | 773 | ||
772 | //This transaction should have the dbi available (creating two write transactions obviously doesn't work) | 774 | //This transaction should have the dbi available (creating two write transactions obviously doesn't work) |
773 | //NOTE: we don't support this scenario. A write transaction must commit or abort before a read transaction opens the same database. | 775 | //NOTE: we don't support this scenario. A write transaction must commit or abort before a read transaction opens the same database. |
774 | // auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); | 776 | // auto transaction3 = store.createTransaction(Sink::Storage::DataStore::ReadOnly); |
775 | // auto db3 = transaction3.openDatabase("testTransactionVisibility", nullptr, false); | 777 | // auto db3 = transaction3.openDatabase("testTransactionVisibility"); |
776 | // QCOMPARE(readValue(db3, "key1"), QByteArray("foo")); | 778 | // QCOMPARE(readValue(db3, "key1"), QByteArray("foo")); |
777 | 779 | ||
778 | //Ensure we can still open further dbis in the write transaction | 780 | //Ensure we can still open further dbis in the write transaction |
779 | auto db4 = transaction2.openDatabase("anotherDb", nullptr, false); | 781 | auto db4 = transaction2.openDatabase("anotherDb"); |
780 | } | 782 | } |
781 | 783 | ||
782 | } | 784 | } |
785 | |||
786 | void testIntegerKeys() | ||
787 | { | ||
788 | const int flags = Sink::Storage::IntegerKeys; | ||
789 | Sink::Storage::DataStore store(testDataPath, | ||
790 | { dbName, { { "test", flags } } }, Sink::Storage::DataStore::ReadWrite); | ||
791 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
792 | auto db = transaction.openDatabase("testIntegerKeys", {}, flags); | ||
793 | db.write(0, "value1"); | ||
794 | db.write(1, "value2"); | ||
795 | |||
796 | size_t resultKey; | ||
797 | QByteArray result; | ||
798 | int numValues = db.scan(0, [&](size_t key, const QByteArray &value) -> bool { | ||
799 | resultKey = key; | ||
800 | result = value; | ||
801 | return true; | ||
802 | }); | ||
803 | |||
804 | QCOMPARE(numValues, 1); | ||
805 | QCOMPARE(resultKey, 0); | ||
806 | QCOMPARE(result, "value1"); | ||
807 | |||
808 | int numValues2 = db.scan(1, [&](size_t key, const QByteArray &value) -> bool { | ||
809 | resultKey = key; | ||
810 | result = value; | ||
811 | return true; | ||
812 | }); | ||
813 | |||
814 | QCOMPARE(numValues2, 1); | ||
815 | QCOMPARE(resultKey, 1); | ||
816 | QCOMPARE(result, "value2"); | ||
817 | } | ||
818 | |||
819 | void testDuplicateIntegerKeys() | ||
820 | { | ||
821 | const int flags = Sink::Storage::IntegerKeys | Sink::Storage::AllowDuplicates; | ||
822 | Sink::Storage::DataStore store(testDataPath, | ||
823 | { dbName, { { "testDuplicateIntegerKeys", flags} } }, | ||
824 | Sink::Storage::DataStore::ReadWrite); | ||
825 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
826 | auto db = transaction.openDatabase("testDuplicateIntegerKeys", {}, flags); | ||
827 | db.write(0, "value1"); | ||
828 | db.write(1, "value2"); | ||
829 | db.write(1, "value3"); | ||
830 | QSet<QByteArray> results; | ||
831 | int numValues = db.scan(1, [&](size_t, const QByteArray &value) -> bool { | ||
832 | results << value; | ||
833 | return true; | ||
834 | }); | ||
835 | |||
836 | QCOMPARE(numValues, 2); | ||
837 | QCOMPARE(results.size(), 2); | ||
838 | QVERIFY(results.contains("value2")); | ||
839 | QVERIFY(results.contains("value3")); | ||
840 | } | ||
841 | |||
842 | void testDuplicateWithIntegerValues() | ||
843 | { | ||
844 | const int flags = Sink::Storage::AllowDuplicates | Sink::Storage::IntegerValues; | ||
845 | Sink::Storage::DataStore store(testDataPath, | ||
846 | { dbName, { { "testDuplicateWithIntegerValues", flags} } }, | ||
847 | Sink::Storage::DataStore::ReadWrite); | ||
848 | |||
849 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
850 | auto db = transaction.openDatabase("testDuplicateWithIntegerValues", {}, flags); | ||
851 | |||
852 | const size_t number1 = 1; | ||
853 | const size_t number2 = 2; | ||
854 | |||
855 | const QByteArray number1BA = Sink::sizeTToByteArray(number1); | ||
856 | const QByteArray number2BA = Sink::sizeTToByteArray(number2); | ||
857 | |||
858 | db.write(0, number1BA); | ||
859 | db.write(1, number2BA); | ||
860 | db.write(1, number1BA); | ||
861 | |||
862 | QList<QByteArray> results; | ||
863 | int numValues = db.scan(1, [&](size_t, const QByteArray &value) -> bool { | ||
864 | results << value; | ||
865 | return true; | ||
866 | }); | ||
867 | |||
868 | QCOMPARE(numValues, 2); | ||
869 | QCOMPARE(results.size(), 2); | ||
870 | QCOMPARE(results[0], number1BA); | ||
871 | QCOMPARE(results[1], number2BA); | ||
872 | } | ||
873 | |||
874 | void testIntegerKeyMultipleOf256() | ||
875 | { | ||
876 | const int flags = Sink::Storage::IntegerKeys; | ||
877 | Sink::Storage::DataStore store(testDataPath, | ||
878 | { dbName, { {"testIntegerKeyMultipleOf256", flags} } }, | ||
879 | Sink::Storage::DataStore::ReadWrite); | ||
880 | |||
881 | { | ||
882 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
883 | auto db = transaction.openDatabase("testIntegerKeyMultipleOf256", {}, flags); | ||
884 | |||
885 | db.write(0x100, "hello"); | ||
886 | db.write(0x200, "hello2"); | ||
887 | db.write(0x42, "hello3"); | ||
888 | |||
889 | transaction.commit(); | ||
890 | } | ||
891 | |||
892 | { | ||
893 | auto transaction2 = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
894 | auto db = transaction2.openDatabase("testIntegerKeyMultipleOf256", {}, flags); | ||
895 | |||
896 | size_t resultKey; | ||
897 | QByteArray resultValue; | ||
898 | db.scan(0x100, [&] (size_t key, const QByteArray &value) { | ||
899 | resultKey = key; | ||
900 | resultValue = value; | ||
901 | return false; | ||
902 | }); | ||
903 | |||
904 | QCOMPARE(resultKey, 0x100); | ||
905 | QCOMPARE(resultValue, "hello"); | ||
906 | } | ||
907 | } | ||
908 | |||
909 | void testIntegerProperlySorted() | ||
910 | { | ||
911 | const int flags = Sink::Storage::IntegerKeys; | ||
912 | Sink::Storage::DataStore store(testDataPath, | ||
913 | { dbName, { {"testIntegerProperlySorted", flags} } }, | ||
914 | Sink::Storage::DataStore::ReadWrite); | ||
915 | |||
916 | { | ||
917 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
918 | auto db = transaction.openDatabase("testIntegerProperlySorted", {}, flags); | ||
919 | |||
920 | for (size_t i = 0; i < 0x100; ++i) { | ||
921 | db.write(i, "hello"); | ||
922 | } | ||
923 | |||
924 | size_t previous = 0; | ||
925 | bool success = true; | ||
926 | db.scan("", [&] (const QByteArray &key, const QByteArray &value) { | ||
927 | size_t current = Sink::byteArrayToSizeT(key); | ||
928 | if (current < previous) { | ||
929 | success = false; | ||
930 | return false; | ||
931 | } | ||
932 | |||
933 | previous = current; | ||
934 | return true; | ||
935 | }); | ||
936 | |||
937 | QVERIFY2(success, "Integer are not properly sorted before commit"); | ||
938 | |||
939 | transaction.commit(); | ||
940 | } | ||
941 | |||
942 | { | ||
943 | auto transaction = store.createTransaction(Sink::Storage::DataStore::ReadWrite); | ||
944 | auto db = transaction.openDatabase("testIntegerProperlySorted", {}, flags); | ||
945 | |||
946 | size_t previous = 0; | ||
947 | bool success = true; | ||
948 | db.scan("", [&] (const QByteArray &key, const QByteArray &value) { | ||
949 | size_t current = Sink::byteArrayToSizeT(key); | ||
950 | if (current < previous) { | ||
951 | success = false; | ||
952 | return false; | ||
953 | } | ||
954 | |||
955 | previous = current; | ||
956 | return true; | ||
957 | }); | ||
958 | |||
959 | QVERIFY2(success, "Integer are not properly sorted after commit"); | ||
960 | } | ||
961 | } | ||
962 | |||
783 | }; | 963 | }; |
784 | 964 | ||
785 | QTEST_MAIN(StorageTest) | 965 | QTEST_MAIN(StorageTest) |