diff options
Diffstat (limited to 'common/storage_lmdb.cpp')
-rw-r--r-- | common/storage_lmdb.cpp | 99 |
1 files changed, 77 insertions, 22 deletions
diff --git a/common/storage_lmdb.cpp b/common/storage_lmdb.cpp index a007405..e3377b2 100644 --- a/common/storage_lmdb.cpp +++ b/common/storage_lmdb.cpp | |||
@@ -48,6 +48,10 @@ static QMutex sCreateDbiLock; | |||
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,13 @@ DataStore::NamedDatabase::~NamedDatabase() | |||
371 | delete d; | 379 | delete d; |
372 | } | 380 | } |
373 | 381 | ||
382 | bool DataStore::NamedDatabase::write(const size_t key, const QByteArray &value, | ||
383 | const std::function<void(const DataStore::Error &error)> &errorHandler) | ||
384 | { | ||
385 | auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key)); | ||
386 | return write(baKey, value, errorHandler); | ||
387 | } | ||
388 | |||
374 | bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) | 389 | bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &sValue, const std::function<void(const DataStore::Error &error)> &errorHandler) |
375 | { | 390 | { |
376 | if (!d || !d->transaction) { | 391 | if (!d || !d->transaction) { |
@@ -407,11 +422,25 @@ bool DataStore::NamedDatabase::write(const QByteArray &sKey, const QByteArray &s | |||
407 | return !rc; | 422 | return !rc; |
408 | } | 423 | } |
409 | 424 | ||
425 | void DataStore::NamedDatabase::remove( | ||
426 | const size_t key, const std::function<void(const DataStore::Error &error)> &errorHandler) | ||
427 | { | ||
428 | auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key)); | ||
429 | return remove(baKey, errorHandler); | ||
430 | } | ||
431 | |||
410 | void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) | 432 | void DataStore::NamedDatabase::remove(const QByteArray &k, const std::function<void(const DataStore::Error &error)> &errorHandler) |
411 | { | 433 | { |
412 | remove(k, QByteArray(), errorHandler); | 434 | remove(k, QByteArray(), errorHandler); |
413 | } | 435 | } |
414 | 436 | ||
437 | void DataStore::NamedDatabase::remove(const size_t key, const QByteArray &value, | ||
438 | const std::function<void(const DataStore::Error &error)> &errorHandler) | ||
439 | { | ||
440 | auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key)); | ||
441 | return remove(baKey, value, errorHandler); | ||
442 | } | ||
443 | |||
415 | void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) | 444 | void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &value, const std::function<void(const DataStore::Error &error)> &errorHandler) |
416 | { | 445 | { |
417 | if (!d || !d->transaction) { | 446 | if (!d || !d->transaction) { |
@@ -445,6 +474,19 @@ void DataStore::NamedDatabase::remove(const QByteArray &k, const QByteArray &val | |||
445 | } | 474 | } |
446 | } | 475 | } |
447 | 476 | ||
477 | int DataStore::NamedDatabase::scan(const size_t key, | ||
478 | const std::function<bool(size_t key, const QByteArray &value)> &resultHandler, | ||
479 | const std::function<void(const DataStore::Error &error)> &errorHandler, bool skipInternalKeys) const | ||
480 | { | ||
481 | auto baKey = QByteArray::fromRawData(reinterpret_cast<const char *>(&key), sizeof(key)); | ||
482 | return scan(baKey, | ||
483 | [&resultHandler](const QByteArray &key, const QByteArray &value) { | ||
484 | size_t integerKey = *reinterpret_cast<const size_t *>(key.constData()); | ||
485 | return resultHandler(integerKey, value); | ||
486 | }, | ||
487 | errorHandler, /* findSubstringKeys = */ false, skipInternalKeys); | ||
488 | } | ||
489 | |||
448 | int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, | 490 | int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool(const QByteArray &key, const QByteArray &value)> &resultHandler, |
449 | const std::function<void(const DataStore::Error &error)> &errorHandler, bool findSubstringKeys, bool skipInternalKeys) const | 491 | const std::function<void(const DataStore::Error &error)> &errorHandler, bool findSubstringKeys, bool skipInternalKeys) const |
450 | { | 492 | { |
@@ -471,8 +513,10 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
471 | 513 | ||
472 | int numberOfRetrievedValues = 0; | 514 | int numberOfRetrievedValues = 0; |
473 | 515 | ||
474 | if (k.isEmpty() || d->allowDuplicates || findSubstringKeys) { | 516 | bool allowDuplicates = d->flags & AllowDuplicates; |
475 | MDB_cursor_op op = d->allowDuplicates ? MDB_SET : MDB_FIRST; | 517 | |
518 | if (k.isEmpty() || allowDuplicates || findSubstringKeys) { | ||
519 | MDB_cursor_op op = allowDuplicates ? MDB_SET : MDB_FIRST; | ||
476 | if (findSubstringKeys) { | 520 | if (findSubstringKeys) { |
477 | op = MDB_SET_RANGE; | 521 | op = MDB_SET_RANGE; |
478 | } | 522 | } |
@@ -490,7 +534,7 @@ int DataStore::NamedDatabase::scan(const QByteArray &k, const std::function<bool | |||
490 | key.mv_data = (void *)k.constData(); | 534 | key.mv_data = (void *)k.constData(); |
491 | key.mv_size = k.size(); | 535 | key.mv_size = k.size(); |
492 | } | 536 | } |
493 | MDB_cursor_op nextOp = (d->allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT; | 537 | MDB_cursor_op nextOp = (allowDuplicates && !findSubstringKeys) ? MDB_NEXT_DUP : MDB_NEXT; |
494 | while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) { | 538 | while ((rc = mdb_cursor_get(cursor, &key, &data, nextOp)) == 0) { |
495 | const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size); | 539 | const auto current = QByteArray::fromRawData((char *)key.mv_data, key.mv_size); |
496 | // Every consequitive lookup simply iterates through the list | 540 | // Every consequitive lookup simply iterates through the list |
@@ -602,6 +646,15 @@ void DataStore::NamedDatabase::findLatest(const QByteArray &k, const std::functi | |||
602 | return; | 646 | return; |
603 | } | 647 | } |
604 | 648 | ||
649 | int DataStore::NamedDatabase::findAllInRange(const size_t lowerBound, const size_t upperBound, | ||
650 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | ||
651 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | ||
652 | { | ||
653 | auto baLowerBound = QByteArray::fromRawData(reinterpret_cast<const char *>(&lowerBound), sizeof(size_t)); | ||
654 | auto baUpperBound = QByteArray::fromRawData(reinterpret_cast<const char *>(&upperBound), sizeof(size_t)); | ||
655 | return findAllInRange(baLowerBound, baUpperBound, resultHandler, errorHandler); | ||
656 | } | ||
657 | |||
605 | int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, | 658 | int DataStore::NamedDatabase::findAllInRange(const QByteArray &lowerBound, const QByteArray &upperBound, |
606 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, | 659 | const std::function<void(const QByteArray &key, const QByteArray &value)> &resultHandler, |
607 | const std::function<void(const DataStore::Error &error)> &errorHandler) const | 660 | const std::function<void(const DataStore::Error &error)> &errorHandler) const |
@@ -862,7 +915,8 @@ static bool ensureCorrectDb(DataStore::NamedDatabase &database, const QByteArray | |||
862 | return !openedTheWrongDatabase; | 915 | return !openedTheWrongDatabase; |
863 | } | 916 | } |
864 | 917 | ||
865 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, const std::function<void(const DataStore::Error &error)> &errorHandler, bool allowDuplicates) const | 918 | DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray &db, |
919 | const std::function<void(const DataStore::Error &error)> &errorHandler, int flags) const | ||
866 | { | 920 | { |
867 | if (!d) { | 921 | if (!d) { |
868 | SinkError() << "Tried to open database on invalid transaction: " << db; | 922 | SinkError() << "Tried to open database on invalid transaction: " << db; |
@@ -871,7 +925,8 @@ DataStore::NamedDatabase DataStore::Transaction::openDatabase(const QByteArray & | |||
871 | Q_ASSERT(d->transaction); | 925 | Q_ASSERT(d->transaction); |
872 | // We don't now if anything changed | 926 | // We don't now if anything changed |
873 | d->implicitCommit = true; | 927 | d->implicitCommit = true; |
874 | auto p = new DataStore::NamedDatabase::Private(db, allowDuplicates, d->defaultErrorHandler, d->name, d->transaction); | 928 | auto p = new DataStore::NamedDatabase::Private( |
929 | db, flags, d->defaultErrorHandler, d->name, d->transaction); | ||
875 | auto ret = p->openDatabase(d->requestedRead, errorHandler); | 930 | auto ret = p->openDatabase(d->requestedRead, errorHandler); |
876 | if (!ret) { | 931 | if (!ret) { |
877 | delete p; | 932 | delete p; |
@@ -1049,11 +1104,11 @@ public: | |||
1049 | 1104 | ||
1050 | //Create dbis from the given layout. | 1105 | //Create dbis from the given layout. |
1051 | for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { | 1106 | for (auto it = layout.tables.constBegin(); it != layout.tables.constEnd(); it++) { |
1052 | const bool allowDuplicates = it.value(); | 1107 | const int flags = it.value(); |
1053 | MDB_dbi dbi = 0; | 1108 | MDB_dbi dbi = 0; |
1054 | const auto db = it.key(); | 1109 | const auto db = it.key(); |
1055 | const auto dbiName = name + db; | 1110 | const auto dbiName = name + db; |
1056 | if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { | 1111 | if (createDbi(transaction, db, readOnly, flags, dbi)) { |
1057 | sDbis.insert(dbiName, dbi); | 1112 | sDbis.insert(dbiName, dbi); |
1058 | } | 1113 | } |
1059 | } | 1114 | } |
@@ -1063,8 +1118,8 @@ public: | |||
1063 | MDB_dbi dbi = 0; | 1118 | MDB_dbi dbi = 0; |
1064 | const auto dbiName = name + db; | 1119 | const auto dbiName = name + db; |
1065 | //We're going to load the flags anyways. | 1120 | //We're going to load the flags anyways. |
1066 | bool allowDuplicates = false; | 1121 | const int flags = 0; |
1067 | if (createDbi(transaction, db, readOnly, allowDuplicates, dbi)) { | 1122 | if (createDbi(transaction, db, readOnly, flags, dbi)) { |
1068 | sDbis.insert(dbiName, dbi); | 1123 | sDbis.insert(dbiName, dbi); |
1069 | } | 1124 | } |
1070 | } | 1125 | } |