diff options
author | Aaron Seigo <aseigo@kde.org> | 2014-12-14 11:59:39 +0100 |
---|---|---|
committer | Aaron Seigo <aseigo@kde.org> | 2014-12-14 11:59:39 +0100 |
commit | a6ed70495f9f3ecb21c26860dda16aadcdc91c3a (patch) | |
tree | 5304b9fdd7fea14dcc49faea77c0ecc848a7525f /common/storage_unqlite.cpp | |
parent | 8969c46cf96884c7304a01b8b7822c1936ab215e (diff) | |
download | sink-a6ed70495f9f3ecb21c26860dda16aadcdc91c3a.tar.gz sink-a6ed70495f9f3ecb21c26860dda16aadcdc91c3a.zip |
and now unqlite storage works
Diffstat (limited to 'common/storage_unqlite.cpp')
-rw-r--r-- | common/storage_unqlite.cpp | 252 |
1 files changed, 159 insertions, 93 deletions
diff --git a/common/storage_unqlite.cpp b/common/storage_unqlite.cpp index d0eaa38..5c5ef09 100644 --- a/common/storage_unqlite.cpp +++ b/common/storage_unqlite.cpp | |||
@@ -5,51 +5,93 @@ | |||
5 | #include <QAtomicInt> | 5 | #include <QAtomicInt> |
6 | #include <QDebug> | 6 | #include <QDebug> |
7 | #include <QDir> | 7 | #include <QDir> |
8 | #include <QFileInfo> | ||
9 | #include <QReadWriteLock> | 8 | #include <QReadWriteLock> |
10 | #include <QString> | 9 | #include <QString> |
11 | #include <QTime> | 10 | #include <QTime> |
12 | 11 | ||
13 | #include <unqlite/unqlite.h> | 12 | #include "unqlite/unqlite.h" |
14 | 13 | ||
15 | class Storage::Private | 14 | class Storage::Private |
16 | { | 15 | { |
17 | public: | 16 | public: |
18 | Private(const QString &storageRoot, const QString &name, AccessMode m); | 17 | Private(const QString &s, const QString &name, AccessMode m); |
19 | ~Private(); | 18 | ~Private(); |
20 | 19 | ||
20 | void reportDbError(const char *functionName); | ||
21 | void reportDbError(const char *functionName, int errorCode, | ||
22 | const std::function<void(const Storage::Error &error)> &errorHandler); | ||
23 | |||
24 | QString storageRoot; | ||
21 | QString name; | 25 | QString name; |
22 | kyotocabinet::TreeDB db; | ||
23 | AccessMode mode; | 26 | AccessMode mode; |
24 | bool dbOpen; | 27 | |
28 | unqlite *db; | ||
25 | bool inTransaction; | 29 | bool inTransaction; |
26 | }; | 30 | }; |
27 | 31 | ||
28 | Storage::Private::Private(const QString &storageRoot, const QString &n, AccessMode m) | 32 | Storage::Private::Private(const QString &s, const QString &n, AccessMode m) |
29 | : name(n), | 33 | : storageRoot(s), |
34 | name(n), | ||
30 | mode(m), | 35 | mode(m), |
31 | dbOpen(false), | 36 | db(0), |
32 | inTransaction(false) | 37 | inTransaction(false) |
33 | { | 38 | { |
39 | const QString fullPath(storageRoot + '/' + name); | ||
34 | QDir dir; | 40 | QDir dir; |
35 | dir.mkdir(storageRoot); | 41 | dir.mkdir(storageRoot); |
36 | 42 | ||
37 | //create file | 43 | //create file |
38 | uint32_t openMode = kyotocabinet::BasicDB::OCREATE | | 44 | int openFlags = UNQLITE_OPEN_CREATE; |
39 | (mode == ReadOnly ? kyotocabinet::BasicDB::OREADER | 45 | if (mode == ReadOnly) { |
40 | : kyotocabinet::BasicDB::OWRITER); | 46 | openFlags |= UNQLITE_OPEN_READONLY | UNQLITE_OPEN_MMAP; |
41 | dbOpen = db.open((storageRoot + "/" + name + ".kch").toStdString(), openMode); | 47 | } else { |
42 | if (!dbOpen) { | 48 | openFlags |= UNQLITE_OPEN_READWRITE; |
43 | std::cerr << "Could not open database: " << db.error().codename(db.error().code()) << " " << db.error().message() << std::endl; | 49 | } |
44 | // TODO: handle error | 50 | |
51 | int rc = unqlite_open(&db, fullPath.toStdString().data(), openFlags); | ||
52 | |||
53 | if (rc != UNQLITE_OK) { | ||
54 | reportDbError("unqlite_open"); | ||
45 | } | 55 | } |
46 | } | 56 | } |
47 | 57 | ||
48 | Storage::Private::~Private() | 58 | Storage::Private::~Private() |
49 | { | 59 | { |
50 | if (dbOpen && inTransaction) { | 60 | unqlite_close(db); |
51 | db.end_transaction(false); | 61 | } |
62 | |||
63 | void Storage::Private::reportDbError(const char *functionName) | ||
64 | { | ||
65 | std::cerr << "ERROR: " << functionName; | ||
66 | if (db) { | ||
67 | const char *errorMessage; | ||
68 | int length; | ||
69 | /* Something goes wrong, extract database error log */ | ||
70 | unqlite_config(db, UNQLITE_CONFIG_ERR_LOG, &errorMessage, &length); | ||
71 | if (length > 0) { | ||
72 | std::cerr << ": " << errorMessage; | ||
73 | } | ||
74 | } | ||
75 | std::cerr << std::endl; | ||
76 | } | ||
77 | |||
78 | void Storage::Private::reportDbError(const char *functionName, int errorCode, | ||
79 | const std::function<void(const Storage::Error &error)> &errorHandler) | ||
80 | { | ||
81 | if (db) { | ||
82 | const char *errorMessage; | ||
83 | int length; | ||
84 | /* Something goes wrong, extract database error log */ | ||
85 | unqlite_config(db, UNQLITE_CONFIG_ERR_LOG, &errorMessage, &length); | ||
86 | if (length > 0) { | ||
87 | Error error(name.toStdString(), errorCode, errorMessage); | ||
88 | errorHandler(error); | ||
89 | return; | ||
90 | } | ||
52 | } | 91 | } |
92 | |||
93 | Error error(name.toStdString(), errorCode, functionName); | ||
94 | errorHandler(error); | ||
53 | } | 95 | } |
54 | 96 | ||
55 | Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode) | 97 | Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mode) |
@@ -59,6 +101,10 @@ Storage::Storage(const QString &storageRoot, const QString &name, AccessMode mod | |||
59 | 101 | ||
60 | Storage::~Storage() | 102 | Storage::~Storage() |
61 | { | 103 | { |
104 | if (d->inTransaction) { | ||
105 | abortTransaction(); | ||
106 | } | ||
107 | |||
62 | delete d; | 108 | delete d; |
63 | } | 109 | } |
64 | 110 | ||
@@ -69,11 +115,7 @@ bool Storage::isInTransaction() const | |||
69 | 115 | ||
70 | bool Storage::startTransaction(AccessMode type) | 116 | bool Storage::startTransaction(AccessMode type) |
71 | { | 117 | { |
72 | if (!d->dbOpen) { | 118 | if (!d->db) { |
73 | return false; | ||
74 | } | ||
75 | |||
76 | if (type == ReadWrite && d->mode != ReadWrite) { | ||
77 | return false; | 119 | return false; |
78 | } | 120 | } |
79 | 121 | ||
@@ -81,141 +123,165 @@ bool Storage::startTransaction(AccessMode type) | |||
81 | return true; | 123 | return true; |
82 | } | 124 | } |
83 | 125 | ||
84 | //TODO handle errors | 126 | d->inTransaction = unqlite_begin(d->db) == UNQLITE_OK; |
85 | d->inTransaction = d->db.begin_transaction(); | 127 | |
128 | if (!d->inTransaction) { | ||
129 | d->reportDbError("unqlite_begin"); | ||
130 | } | ||
131 | |||
86 | return d->inTransaction; | 132 | return d->inTransaction; |
87 | } | 133 | } |
88 | 134 | ||
89 | bool Storage::commitTransaction() | 135 | bool Storage::commitTransaction() |
90 | { | 136 | { |
91 | if (!d->dbOpen) { | 137 | if (!d->db) { |
92 | return false; | 138 | return false; |
93 | } | 139 | } |
94 | 140 | ||
95 | if (!d->inTransaction) { | 141 | if (!d->inTransaction) { |
96 | return false; | 142 | return true; |
97 | } | 143 | } |
98 | 144 | ||
99 | bool success = d->db.end_transaction(true); | 145 | int rc = unqlite_commit(d->db); |
100 | d->inTransaction = false; | 146 | d->inTransaction = false; |
101 | return success; | 147 | |
148 | if (rc != UNQLITE_OK) { | ||
149 | d->reportDbError("unqlite_commit"); | ||
150 | } | ||
151 | |||
152 | return rc == UNQLITE_OK; | ||
102 | } | 153 | } |
103 | 154 | ||
104 | void Storage::abortTransaction() | 155 | void Storage::abortTransaction() |
105 | { | 156 | { |
106 | if (!d->dbOpen || !d->inTransaction) { | 157 | if (!d->db || !d->inTransaction) { |
107 | return; | 158 | return; |
108 | } | 159 | } |
109 | 160 | ||
110 | d->db.end_transaction(false); | 161 | unqlite_rollback(d->db); |
111 | d->inTransaction = false; | 162 | d->inTransaction = false; |
112 | } | 163 | } |
113 | 164 | ||
114 | bool Storage::write(const char *key, size_t keySize, const char *value, size_t valueSize) | 165 | bool Storage::write(const char *key, size_t keySize, const char *value, size_t valueSize) |
115 | { | 166 | { |
116 | if (!d->dbOpen) { | 167 | return write(std::string(key, keySize), std::string(value, valueSize)); |
117 | return false; | ||
118 | } | ||
119 | |||
120 | bool success = d->db.set(key, keySize, value, valueSize); | ||
121 | return success; | ||
122 | } | 168 | } |
123 | 169 | ||
124 | bool Storage::write(const std::string &sKey, const std::string &sValue) | 170 | bool Storage::write(const std::string &sKey, const std::string &sValue) |
125 | { | 171 | { |
126 | if (!d->dbOpen) { | 172 | if (!d->db) { |
127 | return false; | 173 | return false; |
128 | } | 174 | } |
129 | 175 | ||
130 | bool success = d->db.set(sKey, sValue); | 176 | int rc = unqlite_kv_store(d->db, sKey.data(), -1, sValue.data(), sValue.size()); |
131 | return success; | 177 | |
178 | if (rc != UNQLITE_OK) { | ||
179 | d->reportDbError("unqlite_kv_store"); | ||
180 | } | ||
181 | |||
182 | return !rc; | ||
132 | } | 183 | } |
133 | 184 | ||
134 | void Storage::read(const std::string &sKey, | 185 | void Storage::read(const std::string &sKey, |
135 | const std::function<bool(const std::string &value)> &resultHandler, | 186 | const std::function<bool(const std::string &value)> &resultHandler, |
136 | const std::function<void(const Storage::Error &error)> &errorHandler) | 187 | const std::function<void(const Storage::Error &error)> &errorHandler) |
137 | { | 188 | { |
138 | if (!d->dbOpen) { | 189 | read(sKey, |
139 | Error error(d->name.toStdString(), -1, "Not open"); | 190 | [&](void *ptr, int size) -> bool { |
140 | errorHandler(error); | 191 | if (ptr) { |
141 | return; | 192 | const std::string resultValue(static_cast<char*>(ptr), size); |
142 | } | 193 | return resultHandler(resultValue); |
194 | } | ||
143 | 195 | ||
144 | std::string value; | 196 | return true; |
145 | if (sKey.empty()) { | 197 | }, errorHandler); |
146 | kyotocabinet::DB::Cursor *cursor = d->db.cursor(); | 198 | } |
147 | cursor->jump(); | ||
148 | 199 | ||
149 | std::string key, value; | 200 | void Storage::read(const std::string &sKey, |
150 | while (cursor->get_value(&value, true) && resultHandler(value)) {} | 201 | const std::function<bool(void *ptr, int size)> &resultHandler, |
202 | const std::function<void(const Storage::Error &error)> &errorHandler) | ||
203 | { | ||
204 | scan(sKey.data(), sKey.size(), [resultHandler](void *keyPtr, int keySize, void *valuePtr, int valueSize) { | ||
205 | return resultHandler(valuePtr, valueSize); | ||
206 | }, errorHandler); | ||
207 | } | ||
151 | 208 | ||
152 | delete cursor; | 209 | void fetchCursorData(unqlite_kv_cursor *cursor, |
153 | return; | 210 | void **keyBuffer, int *keyBufferLength, void **dataBuffer, unqlite_int64 *dataBufferLength, |
154 | } else { | 211 | const std::function<bool(void *keyPtr, int keySize, void *valuePtr, int valueSize)> &resultHandler) |
155 | if (d->db.get(sKey, &value)) { | 212 | { |
156 | resultHandler(value); | 213 | int keyLength = 0; |
157 | return; | 214 | unqlite_int64 dataLength = 0; |
215 | // now fetch the data sizes | ||
216 | if (unqlite_kv_cursor_key(cursor, nullptr, &keyLength) == UNQLITE_OK && | ||
217 | unqlite_kv_cursor_data(cursor, nullptr, &dataLength) == UNQLITE_OK) { | ||
218 | if (keyLength > *keyBufferLength) { | ||
219 | *keyBuffer = realloc(*keyBuffer, keyLength); | ||
220 | *keyBufferLength = keyLength; | ||
158 | } | 221 | } |
159 | } | ||
160 | 222 | ||
161 | Error error(d->name.toStdString(), d->db.error().code(), d->db.error().message()); | 223 | if (dataLength > *dataBufferLength) { |
162 | errorHandler(error); | 224 | *dataBuffer = realloc(*dataBuffer, dataLength); |
225 | *dataBufferLength = dataLength; | ||
226 | } | ||
227 | |||
228 | if (unqlite_kv_cursor_key(cursor, *keyBuffer, &keyLength) == UNQLITE_OK && | ||
229 | unqlite_kv_cursor_data(cursor, *dataBuffer, &dataLength) == UNQLITE_OK) { | ||
230 | resultHandler(*keyBuffer, keyLength, *dataBuffer, dataLength); | ||
231 | } | ||
232 | } | ||
163 | } | 233 | } |
164 | 234 | ||
165 | void Storage::read(const std::string &sKey, | 235 | void Storage::scan(const char *keyData, uint keySize, |
166 | const std::function<bool(void *ptr, int size)> &resultHandler, | 236 | const std::function<bool(void *keyPtr, int keySize, void *valuePtr, int valueSize)> &resultHandler, |
167 | const std::function<void(const Storage::Error &error)> &errorHandler) | 237 | const std::function<void(const Storage::Error &error)> &errorHandler) |
168 | { | 238 | { |
169 | if (!d->dbOpen) { | 239 | if (!d->db) { |
170 | Error error(d->name.toStdString(), -1, "Not open"); | 240 | Error error(d->name.toStdString(), -1, "Not open"); |
171 | errorHandler(error); | 241 | errorHandler(error); |
172 | return; | 242 | return; |
173 | } | 243 | } |
174 | 244 | ||
175 | size_t valueSize; | 245 | unqlite_kv_cursor *cursor; |
176 | char *valueBuffer; | ||
177 | if (sKey.empty()) { | ||
178 | kyotocabinet::DB::Cursor *cursor = d->db.cursor(); | ||
179 | cursor->jump(); | ||
180 | 246 | ||
181 | while ((valueBuffer = cursor->get_value(&valueSize, true))) { | 247 | int rc = unqlite_kv_cursor_init(d->db, &cursor); |
182 | bool ok = resultHandler(valueBuffer, valueSize); | 248 | if (rc != UNQLITE_OK) { |
183 | delete[] valueBuffer; | 249 | d->reportDbError("unqlite_kv_cursor_init", rc, errorHandler); |
184 | if (!ok) { | 250 | return; |
185 | break; | 251 | } |
186 | } | ||
187 | } | ||
188 | 252 | ||
189 | delete cursor; | 253 | void *keyBuffer = nullptr; |
254 | int keyBufferLength = 0; | ||
255 | void *dataBuffer = nullptr; | ||
256 | //FIXME: 64bit ints, but feeding int lenghts to the callbacks. can result in truncation | ||
257 | unqlite_int64 dataBufferLength = 0; | ||
258 | if (!keyData || keySize == 0) { | ||
259 | for (unqlite_kv_cursor_first_entry(cursor); unqlite_kv_cursor_valid_entry(cursor); unqlite_kv_cursor_next_entry(cursor)) { | ||
260 | fetchCursorData(cursor, &keyBuffer, &keyBufferLength, &dataBuffer, &dataBufferLength, resultHandler); | ||
261 | } | ||
190 | } else { | 262 | } else { |
191 | valueBuffer = d->db.get(sKey.data(), sKey.size(), &valueSize); | 263 | rc = unqlite_kv_cursor_seek(cursor, keyData, keySize, UNQLITE_CURSOR_MATCH_EXACT); |
192 | if (valueBuffer) { | 264 | if (rc == UNQLITE_OK) { |
193 | resultHandler(valueBuffer, valueSize); | 265 | fetchCursorData(cursor, &keyBuffer, &keyBufferLength, &dataBuffer, &dataBufferLength, resultHandler); |
194 | } else { | 266 | } else { |
195 | Error error(d->name.toStdString(), d->db.error().code(), d->db.error().message()); | 267 | std::cout << "couldn't find value " << std::string(keyData, keySize) << std::endl; |
196 | errorHandler(error); | ||
197 | } | 268 | } |
198 | delete[] valueBuffer; | 269 | |
199 | } | 270 | } |
271 | |||
272 | free(keyBuffer); | ||
273 | free(dataBuffer); | ||
274 | unqlite_kv_cursor_release(d->db, cursor); | ||
200 | } | 275 | } |
201 | 276 | ||
202 | qint64 Storage::diskUsage() const | 277 | qint64 Storage::diskUsage() const |
203 | { | 278 | { |
204 | if (!d->dbOpen) { | 279 | QFileInfo info(d->storageRoot + '/' + d->name); |
205 | return 0; | ||
206 | } | ||
207 | |||
208 | QFileInfo info(QString::fromStdString(d->db.path())); | ||
209 | return info.size(); | 280 | return info.size(); |
210 | } | 281 | } |
211 | 282 | ||
212 | void Storage::removeFromDisk() const | 283 | void Storage::removeFromDisk() const |
213 | { | 284 | { |
214 | if (!d->dbOpen) { | 285 | QFile::remove(d->storageRoot + '/' + d->name); |
215 | return; | ||
216 | } | ||
217 | |||
218 | QFileInfo info(QString::fromStdString(d->db.path())); | ||
219 | QDir dir = info.dir(); | ||
220 | dir.remove(info.fileName()); | ||
221 | } | 286 | } |
287 | |||