NeoMutt  2023-05-17-56-ga67199
Teaching an old dog new tricks
DOXYGEN
lmdb.c
Go to the documentation of this file.
1
33#include "config.h"
34#include <stddef.h>
35#include <lmdb.h>
36#include <stdint.h>
37#include "mutt/lib.h"
38#include "lib.h"
39
42#if (UINTPTR_MAX == 0xffffffff)
44static const size_t LMDB_DB_SIZE = 2147483648;
45#elif (UINTPTR_MAX == 0xffffffffffffffff)
47static const size_t LMDB_DB_SIZE = 107374182400;
48#else
49#error
50#endif
51
56{
60};
61
66{
67 MDB_env *env;
68 MDB_txn *txn;
69 MDB_dbi db;
71};
72
78static int mdb_get_r_txn(struct StoreLmdbCtx *ctx)
79{
80 int rc;
81
82 if (ctx->txn && ((ctx->txn_mode == TXN_READ) || (ctx->txn_mode == TXN_WRITE)))
83 return MDB_SUCCESS;
84
85 if (ctx->txn)
86 rc = mdb_txn_renew(ctx->txn);
87 else
88 rc = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->txn);
89
90 if (rc == MDB_SUCCESS)
91 {
92 ctx->txn_mode = TXN_READ;
93 }
94 else
95 {
96 mutt_debug(LL_DEBUG2, "%s: %s\n",
97 ctx->txn ? "mdb_txn_renew" : "mdb_txn_begin", mdb_strerror(rc));
98 }
99
100 return rc;
101}
102
108static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
109{
110 int rc;
111
112 if (ctx->txn)
113 {
114 if (ctx->txn_mode == TXN_WRITE)
115 return MDB_SUCCESS;
116
117 /* Free up the memory for readonly or reset transactions */
118 mdb_txn_abort(ctx->txn);
119 }
120
121 rc = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn);
122 if (rc == MDB_SUCCESS)
123 ctx->txn_mode = TXN_WRITE;
124 else
125 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
126
127 return rc;
128}
129
133static void *store_lmdb_open(const char *path)
134{
135 if (!path)
136 return NULL;
137
138 int rc;
139
140 struct StoreLmdbCtx *ctx = mutt_mem_calloc(1, sizeof(struct StoreLmdbCtx));
141
142 rc = mdb_env_create(&ctx->env);
143 if (rc != MDB_SUCCESS)
144 {
145 mutt_debug(LL_DEBUG2, "mdb_env_create: %s\n", mdb_strerror(rc));
146 FREE(&ctx);
147 return NULL;
148 }
149
150 mdb_env_set_mapsize(ctx->env, LMDB_DB_SIZE);
151
152 rc = mdb_env_open(ctx->env, path, MDB_NOSUBDIR, 0644);
153 if (rc != MDB_SUCCESS)
154 {
155 mutt_debug(LL_DEBUG2, "mdb_env_open: %s\n", mdb_strerror(rc));
156 goto fail_env;
157 }
158
159 rc = mdb_get_r_txn(ctx);
160 if (rc != MDB_SUCCESS)
161 {
162 mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
163 goto fail_env;
164 }
165
166 rc = mdb_dbi_open(ctx->txn, NULL, MDB_CREATE, &ctx->db);
167 if (rc != MDB_SUCCESS)
168 {
169 mutt_debug(LL_DEBUG2, "mdb_dbi_open: %s\n", mdb_strerror(rc));
170 goto fail_dbi;
171 }
172
173 mdb_txn_reset(ctx->txn);
175 return ctx;
176
177fail_dbi:
178 mdb_txn_abort(ctx->txn);
180 ctx->txn = NULL;
181
182fail_env:
183 mdb_env_close(ctx->env);
184 FREE(&ctx);
185 return NULL;
186}
187
191static void *store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
192{
193 if (!store)
194 return NULL;
195
196 MDB_val dkey;
197 MDB_val data;
198
199 struct StoreLmdbCtx *ctx = store;
200
201 dkey.mv_data = (void *) key;
202 dkey.mv_size = klen;
203 data.mv_data = NULL;
204 data.mv_size = 0;
205 int rc = mdb_get_r_txn(ctx);
206 if (rc != MDB_SUCCESS)
207 {
208 ctx->txn = NULL;
209 mutt_debug(LL_DEBUG2, "txn_renew: %s\n", mdb_strerror(rc));
210 return NULL;
211 }
212 rc = mdb_get(ctx->txn, ctx->db, &dkey, &data);
213 if (rc == MDB_NOTFOUND)
214 {
215 return NULL;
216 }
217 if (rc != MDB_SUCCESS)
218 {
219 mutt_debug(LL_DEBUG2, "mdb_get: %s\n", mdb_strerror(rc));
220 return NULL;
221 }
222
223 *vlen = data.mv_size;
224 return data.mv_data;
225}
226
230static void store_lmdb_free(void *store, void **ptr)
231{
232 /* LMDB data is owned by the database */
233}
234
238static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
239{
240 if (!store)
241 return -1;
242
243 MDB_val dkey;
244 MDB_val databuf;
245
246 struct StoreLmdbCtx *ctx = store;
247
248 dkey.mv_data = (void *) key;
249 dkey.mv_size = klen;
250 databuf.mv_data = value;
251 databuf.mv_size = vlen;
252 int rc = mdb_get_w_txn(ctx);
253 if (rc != MDB_SUCCESS)
254 {
255 mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
256 return rc;
257 }
258 rc = mdb_put(ctx->txn, ctx->db, &dkey, &databuf, 0);
259 if (rc != MDB_SUCCESS)
260 {
261 mutt_debug(LL_DEBUG2, "mdb_put: %s\n", mdb_strerror(rc));
262 mdb_txn_abort(ctx->txn);
264 ctx->txn = NULL;
265 }
266 return rc;
267}
268
272static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
273{
274 if (!store)
275 return -1;
276
277 MDB_val dkey;
278
279 struct StoreLmdbCtx *ctx = store;
280
281 dkey.mv_data = (void *) key;
282 dkey.mv_size = klen;
283 int rc = mdb_get_w_txn(ctx);
284 if (rc != MDB_SUCCESS)
285 {
286 mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
287 return rc;
288 }
289 rc = mdb_del(ctx->txn, ctx->db, &dkey, NULL);
290 if ((rc != MDB_SUCCESS) && (rc != MDB_NOTFOUND))
291 {
292 mutt_debug(LL_DEBUG2, "mdb_del: %s\n", mdb_strerror(rc));
293 mdb_txn_abort(ctx->txn);
295 ctx->txn = NULL;
296 }
297
298 return rc;
299}
300
304static void store_lmdb_close(void **ptr)
305{
306 if (!ptr || !*ptr)
307 return;
308
309 struct StoreLmdbCtx *db = *ptr;
310
311 if (db->txn)
312 {
313 if (db->txn_mode == TXN_WRITE)
314 mdb_txn_commit(db->txn);
315 else
316 mdb_txn_abort(db->txn);
317
318 db->txn_mode = TXN_UNINITIALIZED;
319 db->txn = NULL;
320 }
321
322 mdb_env_close(db->env);
323 FREE(ptr);
324}
325
329static const char *store_lmdb_version(void)
330{
331 return "lmdb " MDB_VERSION_STRING;
332}
333
#define mutt_debug(LEVEL,...)
Definition: logging2.h:87
static void store_lmdb_close(void **ptr)
Implements StoreOps::close() -.
Definition: lmdb.c:304
static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
Implements StoreOps::delete_record() -.
Definition: lmdb.c:272
static void * store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
Implements StoreOps::fetch() -.
Definition: lmdb.c:191
static void store_lmdb_free(void *store, void **ptr)
Implements StoreOps::free() -.
Definition: lmdb.c:230
static void * store_lmdb_open(const char *path)
Implements StoreOps::open() -.
Definition: lmdb.c:133
static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
Implements StoreOps::store() -.
Definition: lmdb.c:238
static const char * store_lmdb_version(void)
Implements StoreOps::version() -.
Definition: lmdb.c:329
static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
Get an LMDB write transaction.
Definition: lmdb.c:108
static int mdb_get_r_txn(struct StoreLmdbCtx *ctx)
Get an LMDB read transaction.
Definition: lmdb.c:78
MdbTxnMode
The maximum size of the database file (2GiB).
Definition: lmdb.c:56
@ TXN_READ
Read transaction in progress.
Definition: lmdb.c:58
@ TXN_WRITE
Write transaction in progress.
Definition: lmdb.c:59
@ TXN_UNINITIALIZED
Transaction is uninitialised.
Definition: lmdb.c:57
@ 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:43
Convenience wrapper for the library headers.
Key value store.
#define STORE_BACKEND_OPS(_name)
Definition: lib.h:159
LMDB context.
Definition: lmdb.c:66
MDB_txn * txn
Definition: lmdb.c:68
enum MdbTxnMode txn_mode
Definition: lmdb.c:70
MDB_env * env
Definition: lmdb.c:67
MDB_dbi db
Definition: lmdb.c:69