diff options
-rw-r--r-- | common/storage.h | 56 | ||||
-rw-r--r-- | common/storage_lmdb.cpp | 306 | ||||
-rw-r--r-- | tests/storagetest.cpp | 30 |
3 files changed, 350 insertions, 42 deletions
diff --git a/common/storage.h b/common/storage.h index 09365b0..77f11f9 100644 --- a/common/storage.h +++ b/common/storage.h | |||
@@ -51,8 +51,64 @@ public: | |||
51 | int code; | 51 | int code; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | class Transaction | ||
55 | { | ||
56 | public: | ||
57 | ~Transaction(); | ||
58 | bool commit(const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | ||
59 | void abort(); | ||
60 | |||
61 | /** | ||
62 | * Write a value | ||
63 | */ | ||
64 | bool write(const QByteArray &key, const QByteArray &value, const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | ||
65 | |||
66 | /** | ||
67 | * Remove a value | ||
68 | */ | ||
69 | void remove(const QByteArray &key, | ||
70 | const std::function<void(const Storage::Error &error)> &errorHandler); | ||
71 | /** | ||
72 | * Read values with a given key. | ||
73 | * | ||
74 | * * An empty @param key results in a full scan | ||
75 | * * If duplicates are existing (revisions), all values are returned. | ||
76 | * * The pointers of the returned values are valid during the execution of the @param resultHandler | ||
77 | * | ||
78 | * @return The number of values retrieved. | ||
79 | */ | ||
80 | int scan(const QByteArray &k, | ||
81 | const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | ||
82 | const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | ||
83 | |||
84 | Transaction(Transaction&& other) : d(other.d) | ||
85 | { | ||
86 | d = other.d; | ||
87 | other.d = nullptr; | ||
88 | } | ||
89 | Transaction& operator=(Transaction&& other) { | ||
90 | d = other.d; | ||
91 | other.d = nullptr; | ||
92 | return *this; | ||
93 | } | ||
94 | private: | ||
95 | Transaction(Transaction& other); | ||
96 | Transaction& operator=(Transaction& other); | ||
97 | friend Storage; | ||
98 | class Private; | ||
99 | Transaction(); | ||
100 | Transaction(Private*); | ||
101 | Private *d; | ||
102 | }; | ||
103 | |||
54 | Storage(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly, bool allowDuplicates = false); | 104 | Storage(const QString &storageRoot, const QString &name, AccessMode mode = ReadOnly, bool allowDuplicates = false); |
55 | ~Storage(); | 105 | ~Storage(); |
106 | |||
107 | Transaction createTransaction(AccessMode mode = ReadWrite, | ||
108 | const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | ||
109 | |||
110 | |||
111 | //Old API | ||
56 | bool isInTransaction() const; | 112 | bool isInTransaction() const; |
57 | bool startTransaction(AccessMode mode = ReadWrite, const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | 113 | bool startTransaction(AccessMode mode = ReadWrite, const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); |
58 | bool commitTransaction(const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); | 114 | bool commitTransaction(const std::function<void(const Storage::Error &error)> &errorHandler = std::function<void(const Storage::Error &error)>()); |
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index 7bbf8b5..230409f 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -47,6 +47,220 @@ int getErrorCode(int e) | |||
47 | return -1; | 47 | return -1; |
48 | } | 48 | } |
49 | 49 | ||
50 | |||
51 | |||
52 | class Storage::Transaction::Private | ||
53 | { | ||
54 | public: | ||
55 | Private(MDB_txn *_txn, MDB_dbi _dbi, bool _allowDuplicates, const std::function<void(const Storage::Error &error)> &_defaultErrorHandler, const QString &_name) | ||
56 | : transaction(_txn), | ||
57 | dbi(_dbi), | ||
58 | allowDuplicates(_allowDuplicates), | ||
59 | defaultErrorHandler(_defaultErrorHandler), | ||
60 | name(_name), | ||
61 | implicitCommit(false), | ||
62 | error(false) | ||
63 | { | ||
64 | |||
65 | } | ||
66 | ~Private() | ||
67 | { | ||
68 | |||
69 | } | ||
70 | |||
71 | MDB_txn *transaction; | ||
72 | MDB_dbi dbi; | ||
73 | bool allowDuplicates; | ||
74 | std::function<void(const Storage::Error &error)> defaultErrorHandler; | ||
75 | QString name; | ||
76 | bool implicitCommit; | ||
77 | bool error; | ||
78 | }; | ||
79 | |||
80 | Storage::Transaction::Transaction() | ||
81 | : d(0) | ||
82 | { | ||
83 | |||
84 | } | ||
85 | |||
86 | Storage::Transaction::Transaction(Transaction::Private *prv) | ||
87 | : d(prv) | ||
88 | { | ||
89 | |||
90 | } | ||
91 | |||
92 | Storage::Transaction::~Transaction() | ||
93 | { | ||
94 | if (d && d->transaction) { | ||
95 | if (d->implicitCommit && !d->error) { | ||
96 | commit(); | ||
97 | } else { | ||
98 | mdb_txn_abort(d->transaction); | ||
99 | } | ||
100 | } | ||
101 | delete d; | ||
102 | } | ||
103 | |||
104 | bool Storage::Transaction::commit(const std::function<void(const Storage::Error &error)> &errorHandler) | ||
105 | { | ||
106 | if (!d) { | ||
107 | return false; | ||
108 | } | ||
109 | |||
110 | const int rc = mdb_txn_commit(d->transaction); | ||
111 | if (rc) { | ||
112 | mdb_txn_abort(d->transaction); | ||
113 | Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Error during transaction commit: " + QByteArray(mdb_strerror(rc))); | ||
114 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
115 | } | ||
116 | d->transaction = nullptr; | ||
117 | |||
118 | return !rc; | ||
119 | } | ||
120 | |||
121 | void Storage::Transaction::abort() | ||
122 | { | ||
123 | if (!d || !d->transaction) { | ||
124 | return; | ||
125 | } | ||
126 | |||
127 | mdb_txn_abort(d->transaction); | ||
128 | d->transaction = nullptr; | ||
129 | } | ||
130 | |||
131 | bool Storage::Transaction::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const Storage::Error &error)> &errorHandler) | ||
132 | { | ||
133 | if (!d || !d->transaction) { | ||
134 | return false; | ||
135 | } | ||
136 | const void *keyPtr = sKey.data(); | ||
137 | const size_t keySize = sKey.size(); | ||
138 | const void *valuePtr = sValue.data(); | ||
139 | const size_t valueSize = sValue.size(); | ||
140 | |||
141 | if (!keyPtr || keySize == 0) { | ||
142 | Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Tried to write empty key."); | ||
143 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
144 | return false; | ||
145 | } | ||
146 | |||
147 | int rc; | ||
148 | MDB_val key, data; | ||
149 | key.mv_size = keySize; | ||
150 | key.mv_data = const_cast<void*>(keyPtr); | ||
151 | data.mv_size = valueSize; | ||
152 | data.mv_data = const_cast<void*>(valuePtr); | ||
153 | rc = mdb_put(d->transaction, d->dbi, &key, &data, 0); | ||
154 | |||
155 | if (rc) { | ||
156 | d->error = true; | ||
157 | Error error(d->name.toLatin1(), ErrorCodes::GenericError, "mdb_put: " + QByteArray(mdb_strerror(rc))); | ||
158 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
159 | } else { | ||
160 | d->implicitCommit = true; | ||
161 | } | ||
162 | |||
163 | return !rc; | ||
164 | } | ||
165 | |||
166 | int Storage::Transaction::scan(const QByteArray &k, | ||
167 | const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | ||
168 | const std::function<void(const Storage::Error &error)> &errorHandler) | ||
169 | { | ||
170 | if (!d || !d->transaction) { | ||
171 | Error error(d->name.toLatin1(), ErrorCodes::NotOpen, "Not open"); | ||
172 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | int rc; | ||
177 | MDB_val key; | ||
178 | MDB_val data; | ||
179 | MDB_cursor *cursor; | ||
180 | |||
181 | key.mv_data = (void*)k.constData(); | ||
182 | key.mv_size = k.size(); | ||
183 | |||
184 | rc = mdb_cursor_open(d->transaction, d->dbi, &cursor); | ||
185 | if (rc) { | ||
186 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Error during mdb_cursor open: ") + QByteArray(mdb_strerror(rc))); | ||
187 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | int numberOfRetrievedValues = 0; | ||
192 | |||
193 | if (k.isEmpty() || d->allowDuplicates) { | ||
194 | if ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_SET_RANGE : MDB_FIRST)) == 0) { | ||
195 | numberOfRetrievedValues++; | ||
196 | if (resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) { | ||
197 | while ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_NEXT_DUP : MDB_NEXT)) == 0) { | ||
198 | numberOfRetrievedValues++; | ||
199 | if (!resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) { | ||
200 | break; | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | |||
206 | //We never find the last value | ||
207 | if (rc == MDB_NOTFOUND) { | ||
208 | rc = 0; | ||
209 | } | ||
210 | } else { | ||
211 | if ((rc = mdb_cursor_get(cursor, &key, &data, MDB_SET)) == 0) { | ||
212 | numberOfRetrievedValues++; | ||
213 | resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size)); | ||
214 | } | ||
215 | } | ||
216 | |||
217 | mdb_cursor_close(cursor); | ||
218 | |||
219 | if (rc) { | ||
220 | Error error(d->name.toLatin1(), getErrorCode(rc), QByteArray("Key: ") + k + " : " + QByteArray(mdb_strerror(rc))); | ||
221 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
222 | } | ||
223 | |||
224 | return numberOfRetrievedValues; | ||
225 | } | ||
226 | |||
227 | void Storage::Transaction::remove(const QByteArray &k, | ||
228 | const std::function<void(const Storage::Error &error)> &errorHandler) | ||
229 | { | ||
230 | if (!d) { | ||
231 | Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Not open"); | ||
232 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | int rc; | ||
237 | MDB_val key; | ||
238 | key.mv_size = k.size(); | ||
239 | key.mv_data = const_cast<void*>(static_cast<const void*>(k.data())); | ||
240 | rc = mdb_del(d->transaction, d->dbi, &key, 0); | ||
241 | |||
242 | if (rc) { | ||
243 | d->error = true; | ||
244 | Error error(d->name.toLatin1(), ErrorCodes::GenericError, QString("Error on mdb_del: %1 %2").arg(rc).arg(mdb_strerror(rc)).toLatin1()); | ||
245 | errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); | ||
246 | } else { | ||
247 | d->implicitCommit = true; | ||
248 | } | ||
249 | |||
250 | return; | ||
251 | } | ||
252 | |||
253 | |||
254 | |||
255 | |||
256 | |||
257 | |||
258 | |||
259 | |||
260 | |||
261 | |||
262 | |||
263 | |||
50 | class Storage::Private | 264 | class Storage::Private |
51 | { | 265 | { |
52 | public: | 266 | public: |
@@ -81,33 +295,38 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m, bool | |||
81 | allowDuplicates(duplicates) | 295 | allowDuplicates(duplicates) |
82 | { | 296 | { |
83 | const QString fullPath(storageRoot + '/' + name); | 297 | const QString fullPath(storageRoot + '/' + name); |
84 | QDir dir; | 298 | QFileInfo dirInfo(fullPath); |
85 | dir.mkpath(storageRoot); | 299 | if (!dirInfo.exists() && mode == ReadWrite) { |
86 | dir.mkdir(fullPath); | 300 | QDir().mkpath(fullPath); |
87 | 301 | dirInfo.refresh(); | |
88 | //Ensure the environment is only created once | 302 | } |
89 | QMutexLocker locker(&sMutex); | 303 | if (mode == ReadWrite && !dirInfo.permission(QFile::WriteOwner)) { |
90 | 304 | qCritical() << fullPath << "does not have write permissions. Aborting"; | |
91 | int rc = 0; | 305 | } else if (dirInfo.exists()) { |
92 | /* | 306 | //Ensure the environment is only created once |
93 | * It seems we can only ever have one environment open in the process. | 307 | QMutexLocker locker(&sMutex); |
94 | * Otherwise multi-threading breaks. | 308 | |
95 | */ | 309 | /* |
96 | env = sEnvironments.value(fullPath); | 310 | * It seems we can only ever have one environment open in the process. |
97 | if (!env) { | 311 | * Otherwise multi-threading breaks. |
98 | if ((rc = mdb_env_create(&env))) { | 312 | */ |
99 | // TODO: handle error | 313 | env = sEnvironments.value(fullPath); |
100 | std::cerr << "mdb_env_create: " << rc << " " << mdb_strerror(rc) << std::endl; | 314 | if (!env) { |
101 | } else { | 315 | int rc = 0; |
102 | if ((rc = mdb_env_open(env, fullPath.toStdString().data(), mode == ReadOnly ? MDB_RDONLY : 0 , 0664))) { | 316 | if ((rc = mdb_env_create(&env))) { |
103 | std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl; | 317 | // TODO: handle error |
104 | mdb_env_close(env); | 318 | std::cerr << "mdb_env_create: " << rc << " " << mdb_strerror(rc) << std::endl; |
105 | env = 0; | ||
106 | } else { | 319 | } else { |
107 | //FIXME: dynamic resize | 320 | if ((rc = mdb_env_open(env, fullPath.toStdString().data(), mode == ReadOnly ? MDB_RDONLY : 0 , 0664))) { |
108 | const size_t dbSize = (size_t)10485760 * (size_t)100 * (size_t)80; //10MB * 800 | 321 | std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl; |
109 | mdb_env_set_mapsize(env, dbSize); | 322 | mdb_env_close(env); |
110 | sEnvironments.insert(fullPath, env); | 323 | env = 0; |
324 | } else { | ||
325 | //FIXME: dynamic resize | ||
326 | const size_t dbSize = (size_t)10485760 * (size_t)8000; //1MB * 8000 | ||
327 | mdb_env_set_mapsize(env, dbSize); | ||
328 | sEnvironments.insert(fullPath, env); | ||
329 | } | ||
111 | } | 330 | } |
112 | } | 331 | } |
113 | } | 332 | } |
@@ -142,9 +361,40 @@ bool Storage::exists() const | |||
142 | return (d->env != 0); | 361 | return (d->env != 0); |
143 | } | 362 | } |
144 | 363 | ||
145 | bool Storage::isInTransaction() const | 364 | Storage::Transaction Storage::createTransaction(AccessMode type, const std::function<void(const Storage::Error &error)> &errorHandlerArg) |
146 | { | 365 | { |
147 | return d->transaction; | 366 | auto errorHandler = errorHandlerArg ? errorHandlerArg : defaultErrorHandler(); |
367 | if (!d->env) { | ||
368 | errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Missing database environment")); | ||
369 | return Transaction(); | ||
370 | } | ||
371 | |||
372 | bool requestedRead = type == ReadOnly; | ||
373 | |||
374 | if (d->mode == ReadOnly && !requestedRead) { | ||
375 | errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Requested read/write transaction in read-only mode.")); | ||
376 | return Transaction(); | ||
377 | } | ||
378 | |||
379 | int rc; | ||
380 | MDB_txn *txn; | ||
381 | rc = mdb_txn_begin(d->env, NULL, requestedRead ? MDB_RDONLY : 0, &txn); | ||
382 | if (!rc) { | ||
383 | //TODO: Move opening of dbi into Transaction for different named databases | ||
384 | MDB_dbi dbi; | ||
385 | rc = mdb_dbi_open(txn, NULL, d->allowDuplicates ? MDB_DUPSORT : 0, &dbi); | ||
386 | if (rc) { | ||
387 | errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Error while opening transaction: " + QByteArray(mdb_strerror(rc)))); | ||
388 | return Transaction(); | ||
389 | } | ||
390 | return Transaction(new Transaction::Private(txn, dbi, d->allowDuplicates, defaultErrorHandler(), d->name)); | ||
391 | } else { | ||
392 | if (rc) { | ||
393 | errorHandler(Error(d->name.toLatin1(), ErrorCodes::GenericError, "Error while beginning transaction: " + QByteArray(mdb_strerror(rc)))); | ||
394 | return Transaction(); | ||
395 | } | ||
396 | } | ||
397 | return Transaction(); | ||
148 | } | 398 | } |
149 | 399 | ||
150 | bool Storage::startTransaction(AccessMode type, | 400 | bool Storage::startTransaction(AccessMode type, |
diff --git a/tests/storagetest.cpp b/tests/storagetest.cpp index b088670..b71a767 100644 --- a/tests/storagetest.cpp +++ b/tests/storagetest.cpp | |||
@@ -20,17 +20,18 @@ private: | |||
20 | void populate(int count) | 20 | void populate(int count) |
21 | { | 21 | { |
22 | Akonadi2::Storage storage(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | 22 | Akonadi2::Storage storage(testDataPath, dbName, Akonadi2::Storage::ReadWrite); |
23 | auto transaction = storage.createTransaction(Akonadi2::Storage::ReadWrite); | ||
23 | for (int i = 0; i < count; i++) { | 24 | for (int i = 0; i < count; i++) { |
24 | //This should perhaps become an implementation detail of the db? | 25 | //This should perhaps become an implementation detail of the db? |
25 | if (i % 10000 == 0) { | 26 | if (i % 10000 == 0) { |
26 | if (i > 0) { | 27 | if (i > 0) { |
27 | storage.commitTransaction(); | 28 | transaction.commit(); |
29 | transaction = std::move(storage.createTransaction(Akonadi2::Storage::ReadWrite)); | ||
28 | } | 30 | } |
29 | storage.startTransaction(); | ||
30 | } | 31 | } |
31 | storage.write(keyPrefix + QByteArray::number(i), keyPrefix + QByteArray::number(i)); | 32 | transaction.write(keyPrefix + QByteArray::number(i), keyPrefix + QByteArray::number(i)); |
32 | } | 33 | } |
33 | storage.commitTransaction(); | 34 | transaction.commit(); |
34 | } | 35 | } |
35 | 36 | ||
36 | bool verify(Akonadi2::Storage &storage, int i) | 37 | bool verify(Akonadi2::Storage &storage, int i) |
@@ -38,8 +39,8 @@ private: | |||
38 | bool success = true; | 39 | bool success = true; |
39 | bool keyMatch = true; | 40 | bool keyMatch = true; |
40 | const auto reference = keyPrefix + QByteArray::number(i); | 41 | const auto reference = keyPrefix + QByteArray::number(i); |
41 | storage.scan(keyPrefix + QByteArray::number(i), | 42 | storage.createTransaction(Akonadi2::Storage::ReadOnly).scan(keyPrefix + QByteArray::number(i), |
42 | [&keyMatch, &reference](const QByteArray &value) -> bool { | 43 | [&keyMatch, &reference](const QByteArray &key, const QByteArray &value) -> bool { |
43 | if (value != reference) { | 44 | if (value != reference) { |
44 | qDebug() << "Mismatch while reading"; | 45 | qDebug() << "Mismatch while reading"; |
45 | keyMatch = false; | 46 | keyMatch = false; |
@@ -102,8 +103,8 @@ private Q_SLOTS: | |||
102 | { | 103 | { |
103 | int hit = 0; | 104 | int hit = 0; |
104 | Akonadi2::Storage store(testDataPath, dbName); | 105 | Akonadi2::Storage store(testDataPath, dbName); |
105 | store.scan("", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 106 | store.createTransaction(Akonadi2::Storage::ReadOnly).scan("", [&](const QByteArray &key, const QByteArray &value) -> bool { |
106 | if (std::string(static_cast<char*>(keyValue), keySize) == "key50") { | 107 | if (key == "key50") { |
107 | hit++; | 108 | hit++; |
108 | } | 109 | } |
109 | return true; | 110 | return true; |
@@ -116,8 +117,8 @@ private Q_SLOTS: | |||
116 | int hit = 0; | 117 | int hit = 0; |
117 | bool foundInvalidValue = false; | 118 | bool foundInvalidValue = false; |
118 | Akonadi2::Storage store(testDataPath, dbName); | 119 | Akonadi2::Storage store(testDataPath, dbName); |
119 | store.scan("key50", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 120 | store.createTransaction(Akonadi2::Storage::ReadOnly).scan("key50", [&](const QByteArray &key, const QByteArray &value) -> bool { |
120 | if (std::string(static_cast<char*>(keyValue), keySize) != "key50") { | 121 | if (key != "key50") { |
121 | foundInvalidValue = true; | 122 | foundInvalidValue = true; |
122 | } | 123 | } |
123 | hit++; | 124 | hit++; |
@@ -128,12 +129,13 @@ private Q_SLOTS: | |||
128 | } | 129 | } |
129 | } | 130 | } |
130 | 131 | ||
131 | void testTurnReadToWrite() | 132 | void testNestedOperations() |
132 | { | 133 | { |
133 | populate(3); | 134 | populate(3); |
134 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | 135 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); |
135 | store.scan("key1", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 136 | auto transaction = store.createTransaction(Akonadi2::Storage::ReadWrite); |
136 | store.remove(QByteArray::fromRawData(static_cast<const char*>(keyValue), keySize), [](const Akonadi2::Storage::Error &) { | 137 | transaction.scan("key1", [&](const QByteArray &key, const QByteArray &value) -> bool { |
138 | transaction.remove(key, [](const Akonadi2::Storage::Error &) { | ||
137 | QVERIFY(false); | 139 | QVERIFY(false); |
138 | }); | 140 | }); |
139 | return false; | 141 | return false; |
@@ -145,7 +147,7 @@ private Q_SLOTS: | |||
145 | bool gotResult = false; | 147 | bool gotResult = false; |
146 | bool gotError = false; | 148 | bool gotError = false; |
147 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); | 149 | Akonadi2::Storage store(testDataPath, dbName, Akonadi2::Storage::ReadWrite); |
148 | int numValues = store.scan("", [&](void *keyValue, int keySize, void *dataValue, int dataSize) -> bool { | 150 | int numValues = store.createTransaction(Akonadi2::Storage::ReadOnly).scan("", [&](const QByteArray &key, const QByteArray &value) -> bool { |
149 | gotResult = true; | 151 | gotResult = true; |
150 | return false; | 152 | return false; |
151 | }, | 153 | }, |