summaryrefslogtreecommitdiffstats
path: root/common/storage_lmdb.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/storage_lmdb.cpp')
-rw-r--r--common/storage_lmdb.cpp297
1 files changed, 181 insertions, 116 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp
index 0618d61..ebb3be3 100644
--- a/common/storage_lmdb.cpp
+++ b/common/storage_lmdb.cpp
@@ -47,120 +47,69 @@ int getErrorCode(int e)
47 return -1; 47 return -1;
48} 48}
49 49
50 50class Storage::NamedDatabase::Private
51
52class Storage::Transaction::Private
53{ 51{
54public: 52public:
55 Private(bool _requestRead, bool _allowDuplicates, const std::function<void(const Storage::Error &error)> &_defaultErrorHandler, const QString &_name, MDB_env *_env) 53 Private(const QByteArray &_db, bool _allowDuplicates, const std::function<void(const Storage::Error &error)> &_defaultErrorHandler, const QString &_name, MDB_txn *_txn)
56 : env(_env), 54 : db(_db),
57 requestedRead(_requestRead), 55 transaction(_txn),
58 allowDuplicates(_allowDuplicates), 56 allowDuplicates(_allowDuplicates),
59 defaultErrorHandler(_defaultErrorHandler), 57 defaultErrorHandler(_defaultErrorHandler),
60 name(_name), 58 name(_name)
61 implicitCommit(false),
62 error(false),
63 autoCommitInterval(0),
64 modificationCounter(0)
65 { 59 {
66
67 } 60 }
61
68 ~Private() 62 ~Private()
69 { 63 {
70 64
71 } 65 }
72 66
73 MDB_env *env; 67 QByteArray db;
74 MDB_txn *transaction; 68 MDB_txn *transaction;
75 MDB_dbi dbi; 69 MDB_dbi dbi;
76 bool requestedRead;
77 bool allowDuplicates; 70 bool allowDuplicates;
78 std::function<void(const Storage::Error &error)> defaultErrorHandler; 71 std::function<void(const Storage::Error &error)> defaultErrorHandler;
79 QString name; 72 QString name;
80 bool implicitCommit;
81 bool error;
82 int autoCommitInterval;
83 int modificationCounter;
84 73
85 void startTransaction() 74 bool openDatabase(std::function<void(const Storage::Error &error)> errorHandler)
86 { 75 {
87 const int rc = mdb_txn_begin(env, NULL, requestedRead ? MDB_RDONLY : 0, &transaction); 76 unsigned int flags = MDB_CREATE;
88 if (rc) { 77 if (allowDuplicates) {
89 defaultErrorHandler(Error(name.toLatin1(), ErrorCodes::GenericError, "Error while opening transaction: " + QByteArray(mdb_strerror(rc)))); 78 flags |= MDB_DUPSORT;
90 } 79 }
91 } 80 if (const int rc = mdb_dbi_open(transaction, db.constData(), flags, &dbi)) {
92 81 qWarning() << "Failed to open: " << rc << db;
93 void openDatabase() 82 dbi = 0;
94 { 83 transaction = 0;
95 const int rc = mdb_dbi_open(transaction, NULL, allowDuplicates ? MDB_DUPSORT : 0, &dbi); 84 Error error(name.toLatin1(), ErrorCodes::GenericError, "Error while opening database: " + QByteArray(mdb_strerror(rc)));
96 if (rc) { 85 errorHandler ? errorHandler(error) : defaultErrorHandler(error);
97 defaultErrorHandler(Error(name.toLatin1(), ErrorCodes::GenericError, "Error while opening database: " + QByteArray(mdb_strerror(rc)))); 86 return false;
98 } 87 }
88 return true;
99 } 89 }
100}; 90};
101 91
102Storage::Transaction::Transaction() 92Storage::NamedDatabase::NamedDatabase()
103 : d(0) 93 : d(nullptr)
104{ 94{
105 95
106} 96}
107 97
108Storage::Transaction::Transaction(Transaction::Private *prv) 98Storage::NamedDatabase::NamedDatabase(NamedDatabase::Private *prv)
109 : d(prv) 99 : d(prv)
110{ 100{
111 d->startTransaction();
112 d->openDatabase();
113} 101}
114 102
115Storage::Transaction::~Transaction() 103Storage::NamedDatabase::~NamedDatabase()
116{ 104{
117 if (d && d->transaction) {
118 if (d->implicitCommit && !d->error) {
119 commit();
120 } else {
121 mdb_txn_abort(d->transaction);
122 }
123 }
124 delete d; 105 delete d;
125} 106}
126 107
127bool Storage::Transaction::commit(const std::function<void(const Storage::Error &error)> &errorHandler) 108bool Storage::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const Storage::Error &error)> &errorHandler)
128{
129 if (!d) {
130 return false;
131 }
132
133 const int rc = mdb_txn_commit(d->transaction);
134 if (rc) {
135 mdb_txn_abort(d->transaction);
136 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Error during transaction commit: " + QByteArray(mdb_strerror(rc)));
137 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
138 }
139 d->transaction = nullptr;
140
141 return !rc;
142}
143
144void Storage::Transaction::abort()
145{
146 if (!d || !d->transaction) {
147 return;
148 }
149
150 mdb_txn_abort(d->transaction);
151 d->transaction = nullptr;
152}
153
154void Storage::Transaction::setAutocommit(int interval)
155{
156 if (d) {
157 d->autoCommitInterval = interval;
158 }
159}
160
161bool Storage::Transaction::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const Storage::Error &error)> &errorHandler)
162{ 109{
163 if (!d || !d->transaction) { 110 if (!d || !d->transaction) {
111 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Not open");
112 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
164 return false; 113 return false;
165 } 114 }
166 const void *keyPtr = sKey.data(); 115 const void *keyPtr = sKey.data();
@@ -183,33 +132,43 @@ bool Storage::Transaction::write(const QByteArray &sKey, const QByteArray &sValu
183 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0); 132 rc = mdb_put(d->transaction, d->dbi, &key, &data, 0);
184 133
185 if (rc) { 134 if (rc) {
186 d->error = true;
187 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "mdb_put: " + QByteArray(mdb_strerror(rc))); 135 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "mdb_put: " + QByteArray(mdb_strerror(rc)));
188 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 136 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
189 } else {
190 d->implicitCommit = true;
191 } 137 }
192 138
193 if (d->autoCommitInterval > 0) { 139 return !rc;
194 d->modificationCounter++; 140}
195 if (d->modificationCounter >= d->autoCommitInterval) { 141
196 commit(); 142void Storage::NamedDatabase::remove(const QByteArray &k,
197 d->startTransaction(); 143 const std::function<void(const Storage::Error &error)> &errorHandler)
198 d->openDatabase(); 144{
199 d->modificationCounter = 0; 145 if (!d || !d->transaction) {
200 } 146 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Not open");
147 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
148 return;
201 } 149 }
202 150
203 return !rc; 151 int rc;
152 MDB_val key;
153 key.mv_size = k.size();
154 key.mv_data = const_cast<void*>(static_cast<const void*>(k.data()));
155 rc = mdb_del(d->transaction, d->dbi, &key, 0);
156
157 if (rc) {
158 Error error(d->name.toLatin1(), ErrorCodes::GenericError, QString("Error on mdb_del: %1 %2").arg(rc).arg(mdb_strerror(rc)).toLatin1());
159 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
160 }
161
162 return;
204} 163}
205 164
206int Storage::Transaction::scan(const QByteArray &k, 165int Storage::NamedDatabase::scan(const QByteArray &k,
207 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, 166 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
208 const std::function<void(const Storage::Error &error)> &errorHandler) const 167 const std::function<void(const Storage::Error &error)> &errorHandler) const
209{ 168{
210 if (!d || !d->transaction) { 169 if (!d || !d->transaction) {
211 Error error(d->name.toLatin1(), ErrorCodes::NotOpen, "Not open"); 170 // Error error(d->name.toLatin1(), ErrorCodes::NotOpen, "Not open");
212 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 171 // errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
213 return 0; 172 return 0;
214 } 173 }
215 174
@@ -231,7 +190,7 @@ int Storage::Transaction::scan(const QByteArray &k,
231 int numberOfRetrievedValues = 0; 190 int numberOfRetrievedValues = 0;
232 191
233 if (k.isEmpty() || d->allowDuplicates) { 192 if (k.isEmpty() || d->allowDuplicates) {
234 if ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_SET_RANGE : MDB_FIRST)) == 0) { 193 if ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_SET : MDB_FIRST)) == 0) {
235 numberOfRetrievedValues++; 194 numberOfRetrievedValues++;
236 if (resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) { 195 if (resultHandler(QByteArray::fromRawData((char*)key.mv_data, key.mv_size), QByteArray::fromRawData((char*)data.mv_data, data.mv_size))) {
237 while ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_NEXT_DUP : MDB_NEXT)) == 0) { 196 while ((rc = mdb_cursor_get(cursor, &key, &data, d->allowDuplicates ? MDB_NEXT_DUP : MDB_NEXT)) == 0) {
@@ -264,34 +223,142 @@ int Storage::Transaction::scan(const QByteArray &k,
264 return numberOfRetrievedValues; 223 return numberOfRetrievedValues;
265} 224}
266 225
267void Storage::Transaction::remove(const QByteArray &k, 226
268 const std::function<void(const Storage::Error &error)> &errorHandler) 227
228
229class Storage::Transaction::Private
230{
231public:
232 Private(bool _requestRead, bool _allowDuplicates, const std::function<void(const Storage::Error &error)> &_defaultErrorHandler, const QString &_name, MDB_env *_env)
233 : env(_env),
234 requestedRead(_requestRead),
235 allowDuplicates(_allowDuplicates),
236 defaultErrorHandler(_defaultErrorHandler),
237 name(_name),
238 implicitCommit(false),
239 error(false),
240 modificationCounter(0)
241 {
242
243 }
244 ~Private()
245 {
246
247 }
248
249 MDB_env *env;
250 MDB_txn *transaction;
251 MDB_dbi dbi;
252 bool requestedRead;
253 bool allowDuplicates;
254 std::function<void(const Storage::Error &error)> defaultErrorHandler;
255 QString name;
256 bool implicitCommit;
257 bool error;
258 int modificationCounter;
259
260 void startTransaction()
261 {
262 // qDebug() << "Opening transaction " << requestedRead;
263 const int rc = mdb_txn_begin(env, NULL, requestedRead ? MDB_RDONLY : 0, &transaction);
264 if (rc) {
265 defaultErrorHandler(Error(name.toLatin1(), ErrorCodes::GenericError, "Error while opening transaction: " + QByteArray(mdb_strerror(rc))));
266 }
267 }
268};
269
270Storage::Transaction::Transaction()
271 : d(nullptr)
272{
273
274}
275
276Storage::Transaction::Transaction(Transaction::Private *prv)
277 : d(prv)
278{
279 d->startTransaction();
280}
281
282Storage::Transaction::~Transaction()
283{
284 if (d && d->transaction) {
285 if (d->implicitCommit && !d->error) {
286 // qDebug() << "implicit commit";
287 commit();
288 } else {
289 // qDebug() << "Aorting transaction";
290 mdb_txn_abort(d->transaction);
291 }
292 }
293 delete d;
294}
295
296bool Storage::Transaction::commit(const std::function<void(const Storage::Error &error)> &errorHandler)
269{ 297{
270 if (!d || !d->transaction) { 298 if (!d || !d->transaction) {
271 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Not open"); 299 return false;
300 }
301
302 const int rc = mdb_txn_commit(d->transaction);
303 if (rc) {
304 mdb_txn_abort(d->transaction);
305 Error error(d->name.toLatin1(), ErrorCodes::GenericError, "Error during transaction commit: " + QByteArray(mdb_strerror(rc)));
272 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 306 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
307 }
308 d->transaction = nullptr;
309
310 return !rc;
311}
312
313void Storage::Transaction::abort()
314{
315 if (!d || !d->transaction) {
273 return; 316 return;
274 } 317 }
275 318
276 int rc; 319 mdb_txn_abort(d->transaction);
277 MDB_val key; 320 d->transaction = nullptr;
278 key.mv_size = k.size(); 321}
279 key.mv_data = const_cast<void*>(static_cast<const void*>(k.data()));
280 rc = mdb_del(d->transaction, d->dbi, &key, 0);
281 322
282 if (rc) { 323Storage::NamedDatabase Storage::Transaction::openDatabase(const QByteArray &db, const std::function<void(const Storage::Error &error)> &errorHandler) const
324{
325 if (!d) {
326 return Storage::NamedDatabase();
327 }
328 //We don't now if anything changed
329 d->implicitCommit = true;
330 auto p = new Storage::NamedDatabase::Private(db, d->allowDuplicates, d->defaultErrorHandler, d->name, d->transaction);
331 p->openDatabase(errorHandler);
332 return Storage::NamedDatabase(p);
333}
334
335bool Storage::Transaction::write(const QByteArray &key, const QByteArray &value, const std::function<void(const Storage::Error &error)> &errorHandler)
336{
337 openDatabase().write(key, value, [this, errorHandler](const Storage::Error &error) {
283 d->error = true; 338 d->error = true;
284 Error error(d->name.toLatin1(), ErrorCodes::GenericError, QString("Error on mdb_del: %1 %2").arg(rc).arg(mdb_strerror(rc)).toLatin1());
285 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error); 339 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
286 } else { 340 });
287 d->implicitCommit = true; 341 d->implicitCommit = true;
288 }
289 342
290 return; 343 return !d->error;
291} 344}
292 345
346void Storage::Transaction::remove(const QByteArray &k,
347 const std::function<void(const Storage::Error &error)> &errorHandler)
348{
349 openDatabase().remove(k, [this, errorHandler](const Storage::Error &error) {
350 d->error = true;
351 errorHandler ? errorHandler(error) : d->defaultErrorHandler(error);
352 });
353 d->implicitCommit = true;
354}
293 355
294 356int Storage::Transaction::scan(const QByteArray &k,
357 const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler,
358 const std::function<void(const Storage::Error &error)> &errorHandler) const
359{
360 return openDatabase().scan(k, resultHandler, errorHandler);
361}
295 362
296 363
297 364
@@ -310,9 +377,7 @@ public:
310 QString storageRoot; 377 QString storageRoot;
311 QString name; 378 QString name;
312 379
313 MDB_dbi dbi;
314 MDB_env *env; 380 MDB_env *env;
315 MDB_txn *transaction;
316 AccessMode mode; 381 AccessMode mode;
317 bool readTransaction; 382 bool readTransaction;
318 bool firstOpen; 383 bool firstOpen;
@@ -328,7 +393,6 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m, bool
328 : storageRoot(s), 393 : storageRoot(s),
329 name(n), 394 name(n),
330 env(0), 395 env(0),
331 transaction(0),
332 mode(m), 396 mode(m),
333 readTransaction(false), 397 readTransaction(false),
334 firstOpen(true), 398 firstOpen(true),
@@ -357,7 +421,12 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m, bool
357 // TODO: handle error 421 // TODO: handle error
358 std::cerr << "mdb_env_create: " << rc << " " << mdb_strerror(rc) << std::endl; 422 std::cerr << "mdb_env_create: " << rc << " " << mdb_strerror(rc) << std::endl;
359 } else { 423 } else {
360 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), mode == ReadOnly ? MDB_RDONLY : 0 | MDB_NOTLS, 0664))) { 424 mdb_env_set_maxdbs(env, 10);
425 unsigned int flags = MDB_NOTLS;
426 if (mode == ReadOnly) {
427 flags |= MDB_RDONLY;
428 }
429 if ((rc = mdb_env_open(env, fullPath.toStdString().data(), flags, 0664))) {
361 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl; 430 std::cerr << "mdb_env_open: " << rc << " " << mdb_strerror(rc) << std::endl;
362 mdb_env_close(env); 431 mdb_env_close(env);
363 env = 0; 432 env = 0;
@@ -374,10 +443,6 @@ Storage::Private::Private(const QString &s, const QString &n, AccessMode m, bool
374 443
375Storage::Private::~Private() 444Storage::Private::~Private()
376{ 445{
377 if (transaction) {
378 mdb_txn_abort(transaction);
379 }
380
381 //Since we can have only one environment open per process, we currently leak the environments. 446 //Since we can have only one environment open per process, we currently leak the environments.
382 // if (env) { 447 // if (env) {
383 // //mdb_dbi_close should not be necessary and is potentially dangerous (see docs) 448 // //mdb_dbi_close should not be necessary and is potentially dangerous (see docs)