NeoMutt  2024-03-23-147-g885fbc
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
lmdb.c
Go to the documentation of this file.
1
32#include "config.h"
33#include <stddef.h>
34#include <lmdb.h>
35#include <stdint.h>
36#include "mutt/lib.h"
37#include "lib.h"
38
41#if (UINTPTR_MAX == 0xffffffff)
43static const size_t LMDB_DB_SIZE = 2147483648;
44#elif (UINTPTR_MAX == 0xffffffffffffffff)
46static const size_t LMDB_DB_SIZE = 107374182400;
47#else
48#error
49#endif
50
55{
59};
60
65{
66 MDB_env *env;
67 MDB_txn *txn;
68 MDB_dbi db;
70};
71
76static void lmdb_sdata_free(struct LmdbStoreData **ptr)
77{
78 FREE(ptr);
79}
80
85static struct LmdbStoreData *lmdb_sdata_new(void)
86{
87 return mutt_mem_calloc(1, sizeof(struct LmdbStoreData));
88}
89
95static int lmdb_get_read_txn(struct LmdbStoreData *sdata)
96{
97 int rc;
98
99 if (sdata->txn && ((sdata->txn_mode == TXN_READ) || (sdata->txn_mode == TXN_WRITE)))
100 return MDB_SUCCESS;
101
102 if (sdata->txn)
103 rc = mdb_txn_renew(sdata->txn);
104 else
105 rc = mdb_txn_begin(sdata->env, NULL, MDB_RDONLY, &sdata->txn);
106
107 if (rc == MDB_SUCCESS)
108 {
109 sdata->txn_mode = TXN_READ;
110 }
111 else
112 {
113 mutt_debug(LL_DEBUG2, "%s: %s\n",
114 sdata->txn ? "mdb_txn_renew" : "mdb_txn_begin", mdb_strerror(rc));
115 }
116
117 return rc;
118}
119
125static int lmdb_get_write_txn(struct LmdbStoreData *sdata)
126{
127 if (sdata->txn)
128 {
129 if (sdata->txn_mode == TXN_WRITE)
130 return MDB_SUCCESS;
131
132 /* Free up the memory for readonly or reset transactions */
133 mdb_txn_abort(sdata->txn);
134 }
135
136 int rc = mdb_txn_begin(sdata->env, NULL, 0, &sdata->txn);
137 if (rc == MDB_SUCCESS)
138 sdata->txn_mode = TXN_WRITE;
139 else
140 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
141
142 return rc;
143}
144
148static StoreHandle *store_lmdb_open(const char *path)
149{
150 if (!path)
151 return NULL;
152
153 struct LmdbStoreData *sdata = lmdb_sdata_new();
154
155 int rc = mdb_env_create(&sdata->env);
156 if (rc != MDB_SUCCESS)
157 {
158 mutt_debug(LL_DEBUG2, "mdb_env_create: %s\n", mdb_strerror(rc));
159 lmdb_sdata_free(&sdata);
160 return NULL;
161 }
162
163 mdb_env_set_mapsize(sdata->env, LMDB_DB_SIZE);
164
165 rc = mdb_env_open(sdata->env, path, MDB_NOSUBDIR, 0644);
166 if (rc != MDB_SUCCESS)
167 {
168 mutt_debug(LL_DEBUG2, "mdb_env_open: %s\n", mdb_strerror(rc));
169 goto fail_env;
170 }
171
172 rc = lmdb_get_read_txn(sdata);
173 if (rc != MDB_SUCCESS)
174 {
175 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
176 goto fail_env;
177 }
178
179 rc = mdb_dbi_open(sdata->txn, NULL, MDB_CREATE, &sdata->db);
180 if (rc != MDB_SUCCESS)
181 {
182 mutt_debug(LL_DEBUG2, "mdb_dbi_open: %s\n", mdb_strerror(rc));
183 goto fail_dbi;
184 }
185
186 mdb_txn_reset(sdata->txn);
188 // Return an opaque pointer
189 return (StoreHandle *) sdata;
190
191fail_dbi:
192 mdb_txn_abort(sdata->txn);
194 sdata->txn = NULL;
195
196fail_env:
197 mdb_env_close(sdata->env);
198 lmdb_sdata_free(&sdata);
199 return NULL;
200}
201
205static void *store_lmdb_fetch(StoreHandle *store, const char *key, size_t klen, size_t *vlen)
206{
207 if (!store)
208 return NULL;
209
210 MDB_val dkey = { 0 };
211 MDB_val data = { 0 };
212
213 // Decloak an opaque pointer
214 struct LmdbStoreData *sdata = store;
215
216 dkey.mv_data = (void *) key;
217 dkey.mv_size = klen;
218 data.mv_data = NULL;
219 data.mv_size = 0;
220 int rc = lmdb_get_read_txn(sdata);
221 if (rc != MDB_SUCCESS)
222 {
223 sdata->txn = NULL;
224 mutt_debug(LL_DEBUG2, "txn_renew: %s\n", mdb_strerror(rc));
225 return NULL;
226 }
227 rc = mdb_get(sdata->txn, sdata->db, &dkey, &data);
228 if (rc == MDB_NOTFOUND)
229 {
230 return NULL;
231 }
232 if (rc != MDB_SUCCESS)
233 {
234 mutt_debug(LL_DEBUG2, "mdb_get: %s\n", mdb_strerror(rc));
235 return NULL;
236 }
237
238 *vlen = data.mv_size;
239 return data.mv_data;
240}
241
245static void store_lmdb_free(StoreHandle *store, void **ptr)
246{
247 /* LMDB data is owned by the database */
248}
249
253static int store_lmdb_store(StoreHandle *store, const char *key, size_t klen,
254 void *value, size_t vlen)
255{
256 if (!store)
257 return -1;
258
259 MDB_val dkey = { 0 };
260 MDB_val databuf = { 0 };
261
262 // Decloak an opaque pointer
263 struct LmdbStoreData *sdata = store;
264
265 dkey.mv_data = (void *) key;
266 dkey.mv_size = klen;
267 databuf.mv_data = value;
268 databuf.mv_size = vlen;
269 int rc = lmdb_get_write_txn(sdata);
270 if (rc != MDB_SUCCESS)
271 {
272 mutt_debug(LL_DEBUG2, "lmdb_get_write_txn: %s\n", mdb_strerror(rc));
273 return rc;
274 }
275 rc = mdb_put(sdata->txn, sdata->db, &dkey, &databuf, 0);
276 if (rc != MDB_SUCCESS)
277 {
278 mutt_debug(LL_DEBUG2, "mdb_put: %s\n", mdb_strerror(rc));
279 mdb_txn_abort(sdata->txn);
281 sdata->txn = NULL;
282 }
283 return rc;
284}
285
289static int store_lmdb_delete_record(StoreHandle *store, const char *key, size_t klen)
290{
291 if (!store)
292 return -1;
293
294 MDB_val dkey = { 0 };
295
296 // Decloak an opaque pointer
297 struct LmdbStoreData *sdata = store;
298
299 dkey.mv_data = (void *) key;
300 dkey.mv_size = klen;
301 int rc = lmdb_get_write_txn(sdata);
302 if (rc != MDB_SUCCESS)
303 {
304 mutt_debug(LL_DEBUG2, "lmdb_get_write_txn: %s\n", mdb_strerror(rc));
305 return rc;
306 }
307 rc = mdb_del(sdata->txn, sdata->db, &dkey, NULL);
308 if ((rc != MDB_SUCCESS) && (rc != MDB_NOTFOUND))
309 {
310 mutt_debug(LL_DEBUG2, "mdb_del: %s\n", mdb_strerror(rc));
311 mdb_txn_abort(sdata->txn);
313 sdata->txn = NULL;
314 }
315
316 return rc;
317}
318
323{
324 if (!ptr || !*ptr)
325 return;
326
327 // Decloak an opaque pointer
328 struct LmdbStoreData *sdata = *ptr;
329
330 if (sdata->txn)
331 {
332 if (sdata->txn_mode == TXN_WRITE)
333 mdb_txn_commit(sdata->txn);
334 else
335 mdb_txn_abort(sdata->txn);
336
338 sdata->txn = NULL;
339 }
340
341 mdb_env_close(sdata->env);
342 lmdb_sdata_free((struct LmdbStoreData **) ptr);
343}
344
348static const char *store_lmdb_version(void)
349{
350 return "lmdb " MDB_VERSION_STRING;
351}
352
#define mutt_debug(LEVEL,...)
Definition: logging2.h:89
static void store_lmdb_close(StoreHandle **ptr)
Close a Store connection - Implements StoreOps::close() -.
Definition: lmdb.c:322
static int store_lmdb_delete_record(StoreHandle *store, const char *key, size_t klen)
Delete a record from the Store - Implements StoreOps::delete_record() -.
Definition: lmdb.c:289
static void * store_lmdb_fetch(StoreHandle *store, const char *key, size_t klen, size_t *vlen)
Fetch a Value from the Store - Implements StoreOps::fetch() -.
Definition: lmdb.c:205
static void store_lmdb_free(StoreHandle *store, void **ptr)
Free a Value returned by fetch() - Implements StoreOps::free() -.
Definition: lmdb.c:245
static StoreHandle * store_lmdb_open(const char *path)
Open a connection to a Store - Implements StoreOps::open() -.
Definition: lmdb.c:148
static int store_lmdb_store(StoreHandle *store, const char *key, size_t klen, void *value, size_t vlen)
Write a Value to the Store - Implements StoreOps::store() -.
Definition: lmdb.c:253
static const char * store_lmdb_version(void)
Get a Store version string - Implements StoreOps::version() -.
Definition: lmdb.c:348
static struct LmdbStoreData * lmdb_sdata_new(void)
Create new Lmdb Store Data.
Definition: lmdb.c:85
static int lmdb_get_write_txn(struct LmdbStoreData *sdata)
Get an LMDB write transaction.
Definition: lmdb.c:125
static void lmdb_sdata_free(struct LmdbStoreData **ptr)
Free Lmdb Store Data.
Definition: lmdb.c:76
static int lmdb_get_read_txn(struct LmdbStoreData *sdata)
Get an LMDB read transaction.
Definition: lmdb.c:95
LmdbTxnMode
The maximum size of the database file (2GiB).
Definition: lmdb.c:55
@ TXN_READ
Read transaction in progress.
Definition: lmdb.c:57
@ TXN_WRITE
Write transaction in progress.
Definition: lmdb.c:58
@ TXN_UNINITIALIZED
Transaction is uninitialised.
Definition: lmdb.c:56
@ LL_DEBUG2
Log at debug level 2.
Definition: logging2.h:44
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
#define FREE(x)
Definition: memory.h:45
Convenience wrapper for the library headers.
Key value store.
void StoreHandle
Opaque type for store backend.
Definition: lib.h:61
#define STORE_BACKEND_OPS(_name)
Definition: lib.h:162
LMDB store.
Definition: lmdb.c:65
MDB_txn * txn
Definition: lmdb.c:67
MDB_env * env
Definition: lmdb.c:66
MDB_dbi db
Definition: lmdb.c:68
enum LmdbTxnMode txn_mode
Definition: lmdb.c:69