NeoMutt  2025-12-11-694-ga89709
Teaching an old dog new tricks
DOXYGEN
Loading...
Searching...
No Matches
lmdb.c
Go to the documentation of this file.
1
24
31
32#include "config.h"
33#include <lmdb.h>
34#include <stdbool.h>
35#include <stddef.h>
36#include <stdint.h>
37#include <unistd.h>
38#include "mutt/lib.h"
39#include "lib.h"
40
43#if (UINTPTR_MAX == 0xffffffff)
45static const size_t LMDB_DB_SIZE = 2147483648;
46#elif (UINTPTR_MAX == 0xffffffffffffffff)
48static const size_t LMDB_DB_SIZE = 107374182400;
49#else
50#error
51#endif
52
62
67{
68 MDB_env *env;
69 MDB_txn *txn;
70 MDB_dbi db;
72};
73
78static void lmdb_sdata_free(struct LmdbStoreData **ptr)
79{
80 FREE(ptr);
81}
82
87static struct LmdbStoreData *lmdb_sdata_new(void)
88{
89 return MUTT_MEM_CALLOC(1, struct LmdbStoreData);
90}
91
97static int lmdb_get_read_txn(struct LmdbStoreData *sdata)
98{
99 int rc;
100
101 if (sdata->txn && ((sdata->txn_mode == TXN_READ) || (sdata->txn_mode == TXN_WRITE)))
102 return MDB_SUCCESS;
103
104 if (sdata->txn)
105 rc = mdb_txn_renew(sdata->txn);
106 else
107 rc = mdb_txn_begin(sdata->env, NULL, MDB_RDONLY, &sdata->txn);
108
109 if (rc == MDB_SUCCESS)
110 {
111 sdata->txn_mode = TXN_READ;
112 }
113 else
114 {
115 mutt_debug(LL_DEBUG2, "%s: %s\n",
116 sdata->txn ? "mdb_txn_renew" : "mdb_txn_begin", mdb_strerror(rc));
117 }
118
119 return rc;
120}
121
127static int lmdb_get_write_txn(struct LmdbStoreData *sdata)
128{
129 if (sdata->txn)
130 {
131 if (sdata->txn_mode == TXN_WRITE)
132 return MDB_SUCCESS;
133
134 /* Free up the memory for readonly or reset transactions */
135 mdb_txn_abort(sdata->txn);
136 }
137
138 int rc = mdb_txn_begin(sdata->env, NULL, 0, &sdata->txn);
139 if (rc == MDB_SUCCESS)
140 sdata->txn_mode = TXN_WRITE;
141 else
142 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
143
144 return rc;
145}
146
150static StoreHandle *store_lmdb_open(const char *path, bool create)
151{
152 if (!path)
153 return NULL;
154
155 if (!create && access(path, F_OK) != 0)
156 {
157 return NULL;
158 }
159
160 struct LmdbStoreData *sdata = lmdb_sdata_new();
161
162 int rc = mdb_env_create(&sdata->env);
163 if (rc != MDB_SUCCESS)
164 {
165 mutt_debug(LL_DEBUG2, "mdb_env_create: %s\n", mdb_strerror(rc));
166 lmdb_sdata_free(&sdata);
167 return NULL;
168 }
169
170 mdb_env_set_mapsize(sdata->env, LMDB_DB_SIZE);
171
172 rc = mdb_env_open(sdata->env, path, MDB_NOSUBDIR, 0600);
173 if (rc != MDB_SUCCESS)
174 {
175 mutt_debug(LL_DEBUG2, "mdb_env_open: %s\n", mdb_strerror(rc));
176 goto fail_env;
177 }
178
179 rc = lmdb_get_read_txn(sdata);
180 if (rc != MDB_SUCCESS)
181 {
182 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
183 goto fail_env;
184 }
185
186 rc = mdb_dbi_open(sdata->txn, NULL, MDB_CREATE, &sdata->db);
187 if (rc != MDB_SUCCESS)
188 {
189 mutt_debug(LL_DEBUG2, "mdb_dbi_open: %s\n", mdb_strerror(rc));
190 goto fail_dbi;
191 }
192
193 mdb_txn_reset(sdata->txn);
195 // Return an opaque pointer
196 return (StoreHandle *) sdata;
197
198fail_dbi:
199 mdb_txn_abort(sdata->txn);
201 sdata->txn = NULL;
202
203fail_env:
204 mdb_env_close(sdata->env);
205 lmdb_sdata_free(&sdata);
206 return NULL;
207}
208
212static void *store_lmdb_fetch(StoreHandle *store, const char *key, size_t klen, size_t *vlen)
213{
214 if (!store)
215 return NULL;
216
217 *vlen = 0;
218
219 MDB_val dkey = { 0 };
220 MDB_val data = { 0 };
221
222 // Decloak an opaque pointer
223 struct LmdbStoreData *sdata = store;
224
225 dkey.mv_data = (void *) key;
226 dkey.mv_size = klen;
227 data.mv_data = NULL;
228 data.mv_size = 0;
229 int rc = lmdb_get_read_txn(sdata);
230 if (rc != MDB_SUCCESS)
231 {
232 sdata->txn = NULL;
233 mutt_debug(LL_DEBUG2, "txn_renew: %s\n", mdb_strerror(rc));
234 return NULL;
235 }
236 rc = mdb_get(sdata->txn, sdata->db, &dkey, &data);
237 if (rc == MDB_NOTFOUND)
238 {
239 return NULL;
240 }
241 if (rc != MDB_SUCCESS)
242 {
243 mutt_debug(LL_DEBUG2, "mdb_get: %s\n", mdb_strerror(rc));
244 return NULL;
245 }
246
247 *vlen = data.mv_size;
248 return data.mv_data;
249}
250
254static void store_lmdb_free(StoreHandle *store, void **ptr)
255{
256 /* LMDB data is owned by the database */
257}
258
262static int store_lmdb_store(StoreHandle *store, const char *key, size_t klen,
263 void *value, size_t vlen)
264{
265 if (!store)
266 return -1;
267
268 MDB_val dkey = { 0 };
269 MDB_val databuf = { 0 };
270
271 // Decloak an opaque pointer
272 struct LmdbStoreData *sdata = store;
273
274 dkey.mv_data = (void *) key;
275 dkey.mv_size = klen;
276 databuf.mv_data = value;
277 databuf.mv_size = vlen;
278 int rc = lmdb_get_write_txn(sdata);
279 if (rc != MDB_SUCCESS)
280 {
281 mutt_debug(LL_DEBUG2, "lmdb_get_write_txn: %s\n", mdb_strerror(rc));
282 return rc;
283 }
284 rc = mdb_put(sdata->txn, sdata->db, &dkey, &databuf, 0);
285 if (rc != MDB_SUCCESS)
286 {
287 mutt_debug(LL_DEBUG2, "mdb_put: %s\n", mdb_strerror(rc));
288 mdb_txn_abort(sdata->txn);
290 sdata->txn = NULL;
291 }
292 return rc;
293}
294
298static int store_lmdb_delete_record(StoreHandle *store, const char *key, size_t klen)
299{
300 if (!store)
301 return -1;
302
303 MDB_val dkey = { 0 };
304
305 // Decloak an opaque pointer
306 struct LmdbStoreData *sdata = store;
307
308 dkey.mv_data = (void *) key;
309 dkey.mv_size = klen;
310 int rc = lmdb_get_write_txn(sdata);
311 if (rc != MDB_SUCCESS)
312 {
313 mutt_debug(LL_DEBUG2, "lmdb_get_write_txn: %s\n", mdb_strerror(rc));
314 return rc;
315 }
316 rc = mdb_del(sdata->txn, sdata->db, &dkey, NULL);
317 if ((rc != MDB_SUCCESS) && (rc != MDB_NOTFOUND))
318 {
319 mutt_debug(LL_DEBUG2, "mdb_del: %s\n", mdb_strerror(rc));
320 mdb_txn_abort(sdata->txn);
322 sdata->txn = NULL;
323 }
324
325 return rc;
326}
327
332{
333 if (!ptr || !*ptr)
334 return;
335
336 // Decloak an opaque pointer
337 struct LmdbStoreData *sdata = *ptr;
338
339 if (sdata->txn)
340 {
341 if (sdata->txn_mode == TXN_WRITE)
342 mdb_txn_commit(sdata->txn);
343 else
344 mdb_txn_abort(sdata->txn);
345
347 sdata->txn = NULL;
348 }
349
350 mdb_env_close(sdata->env);
351 lmdb_sdata_free((struct LmdbStoreData **) ptr);
352}
353
357static const char *store_lmdb_version(void)
358{
359 return "lmdb " MDB_VERSION_STRING;
360}
361
#define mutt_debug(LEVEL,...)
Definition logging2.h:91
static void store_lmdb_close(StoreHandle **ptr)
Close a Store connection - Implements StoreOps::close() -.
Definition lmdb.c:331
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:298
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:212
static void store_lmdb_free(StoreHandle *store, void **ptr)
Free a Value returned by fetch() - Implements StoreOps::free() -.
Definition lmdb.c:254
static StoreHandle * store_lmdb_open(const char *path, bool create)
Open a connection to a Store - Implements StoreOps::open() -.
Definition lmdb.c:150
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:262
static const char * store_lmdb_version(void)
Get a Store version string - Implements StoreOps::version() -.
Definition lmdb.c:357
static struct LmdbStoreData * lmdb_sdata_new(void)
Create new Lmdb Store Data.
Definition lmdb.c:87
static int lmdb_get_write_txn(struct LmdbStoreData *sdata)
Get an LMDB write transaction.
Definition lmdb.c:127
static void lmdb_sdata_free(struct LmdbStoreData **ptr)
Free Lmdb Store Data.
Definition lmdb.c:78
static int lmdb_get_read_txn(struct LmdbStoreData *sdata)
Get an LMDB read transaction.
Definition lmdb.c:97
LmdbTxnMode
The maximum size of the database file (2GiB).
Definition lmdb.c:57
@ TXN_READ
Read transaction in progress.
Definition lmdb.c:59
@ TXN_WRITE
Write transaction in progress.
Definition lmdb.c:60
@ TXN_UNINITIALIZED
Transaction is uninitialised.
Definition lmdb.c:58
@ LL_DEBUG2
Log at debug level 2.
Definition logging2.h:46
#define FREE(x)
Free memory and set the pointer to NULL.
Definition memory.h:68
#define MUTT_MEM_CALLOC(n, type)
Definition memory.h:52
Convenience wrapper for the library headers.
Key value store.
void StoreHandle
Opaque type for store backend.
Definition lib.h:58
#define STORE_BACKEND_OPS(_name)
Definition lib.h:160
LMDB store.
Definition lmdb.c:67
MDB_txn * txn
LMDB transaction.
Definition lmdb.c:69
MDB_env * env
LMDB environment.
Definition lmdb.c:68
MDB_dbi db
LMDB database.
Definition lmdb.c:70
enum LmdbTxnMode txn_mode
Transaction mode.
Definition lmdb.c:71