NeoMutt  2022-04-29-81-g9c5a59
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)
43 // 32-bit, limit to 2GiB
44 const size_t LMDB_DB_SIZE = 2147483648;
45 #elif (UINTPTR_MAX == 0xffffffffffffffff)
46 // 64 bit, limit to 100GiB
47 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;
70  enum MdbTxnMode txn_mode;
71 };
72 
78 static 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  ctx->txn_mode = TXN_READ;
92  else
93  {
94  mutt_debug(LL_DEBUG2, "%s: %s\n",
95  ctx->txn ? "mdb_txn_renew" : "mdb_txn_begin", mdb_strerror(rc));
96  }
97 
98  return rc;
99 }
100 
106 static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
107 {
108  int rc;
109 
110  if (ctx->txn)
111  {
112  if (ctx->txn_mode == TXN_WRITE)
113  return MDB_SUCCESS;
114 
115  /* Free up the memory for readonly or reset transactions */
116  mdb_txn_abort(ctx->txn);
117  }
118 
119  rc = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn);
120  if (rc == MDB_SUCCESS)
121  ctx->txn_mode = TXN_WRITE;
122  else
123  mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
124 
125  return rc;
126 }
127 
131 static void *store_lmdb_open(const char *path)
132 {
133  if (!path)
134  return NULL;
135 
136  int rc;
137 
138  struct StoreLmdbCtx *ctx = mutt_mem_calloc(1, sizeof(struct StoreLmdbCtx));
139 
140  rc = mdb_env_create(&ctx->env);
141  if (rc != MDB_SUCCESS)
142  {
143  mutt_debug(LL_DEBUG2, "mdb_env_create: %s\n", mdb_strerror(rc));
144  FREE(&ctx);
145  return NULL;
146  }
147 
148  mdb_env_set_mapsize(ctx->env, LMDB_DB_SIZE);
149 
150  rc = mdb_env_open(ctx->env, path, MDB_NOSUBDIR, 0644);
151  if (rc != MDB_SUCCESS)
152  {
153  mutt_debug(LL_DEBUG2, "mdb_env_open: %s\n", mdb_strerror(rc));
154  goto fail_env;
155  }
156 
157  rc = mdb_get_r_txn(ctx);
158  if (rc != MDB_SUCCESS)
159  {
160  mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
161  goto fail_env;
162  }
163 
164  rc = mdb_dbi_open(ctx->txn, NULL, MDB_CREATE, &ctx->db);
165  if (rc != MDB_SUCCESS)
166  {
167  mutt_debug(LL_DEBUG2, "mdb_dbi_open: %s\n", mdb_strerror(rc));
168  goto fail_dbi;
169  }
170 
171  mdb_txn_reset(ctx->txn);
173  return ctx;
174 
175 fail_dbi:
176  mdb_txn_abort(ctx->txn);
178  ctx->txn = NULL;
179 
180 fail_env:
181  mdb_env_close(ctx->env);
182  FREE(&ctx);
183  return NULL;
184 }
185 
189 static void *store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
190 {
191  if (!store)
192  return NULL;
193 
194  MDB_val dkey;
195  MDB_val data;
196 
197  struct StoreLmdbCtx *ctx = store;
198 
199  dkey.mv_data = (void *) key;
200  dkey.mv_size = klen;
201  data.mv_data = NULL;
202  data.mv_size = 0;
203  int rc = mdb_get_r_txn(ctx);
204  if (rc != MDB_SUCCESS)
205  {
206  ctx->txn = NULL;
207  mutt_debug(LL_DEBUG2, "txn_renew: %s\n", mdb_strerror(rc));
208  return NULL;
209  }
210  rc = mdb_get(ctx->txn, ctx->db, &dkey, &data);
211  if (rc == MDB_NOTFOUND)
212  {
213  return NULL;
214  }
215  if (rc != MDB_SUCCESS)
216  {
217  mutt_debug(LL_DEBUG2, "mdb_get: %s\n", mdb_strerror(rc));
218  return NULL;
219  }
220 
221  *vlen = data.mv_size;
222  return data.mv_data;
223 }
224 
228 static void store_lmdb_free(void *store, void **ptr)
229 {
230  /* LMDB data is owned by the database */
231 }
232 
236 static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
237 {
238  if (!store)
239  return -1;
240 
241  MDB_val dkey;
242  MDB_val databuf;
243 
244  struct StoreLmdbCtx *ctx = store;
245 
246  dkey.mv_data = (void *) key;
247  dkey.mv_size = klen;
248  databuf.mv_data = value;
249  databuf.mv_size = vlen;
250  int rc = mdb_get_w_txn(ctx);
251  if (rc != MDB_SUCCESS)
252  {
253  mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
254  return rc;
255  }
256  rc = mdb_put(ctx->txn, ctx->db, &dkey, &databuf, 0);
257  if (rc != MDB_SUCCESS)
258  {
259  mutt_debug(LL_DEBUG2, "mdb_put: %s\n", mdb_strerror(rc));
260  mdb_txn_abort(ctx->txn);
262  ctx->txn = NULL;
263  }
264  return rc;
265 }
266 
270 static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
271 {
272  if (!store)
273  return -1;
274 
275  MDB_val dkey;
276 
277  struct StoreLmdbCtx *ctx = store;
278 
279  dkey.mv_data = (void *) key;
280  dkey.mv_size = klen;
281  int rc = mdb_get_w_txn(ctx);
282  if (rc != MDB_SUCCESS)
283  {
284  mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
285  return rc;
286  }
287  rc = mdb_del(ctx->txn, ctx->db, &dkey, NULL);
288  if ((rc != MDB_SUCCESS) && (rc != MDB_NOTFOUND))
289  {
290  mutt_debug(LL_DEBUG2, "mdb_del: %s\n", mdb_strerror(rc));
291  mdb_txn_abort(ctx->txn);
293  ctx->txn = NULL;
294  }
295 
296  return rc;
297 }
298 
302 static void store_lmdb_close(void **ptr)
303 {
304  if (!ptr || !*ptr)
305  return;
306 
307  struct StoreLmdbCtx *db = *ptr;
308 
309  if (db->txn)
310  {
311  if (db->txn_mode == TXN_WRITE)
312  mdb_txn_commit(db->txn);
313  else
314  mdb_txn_abort(db->txn);
315 
316  db->txn_mode = TXN_UNINITIALIZED;
317  db->txn = NULL;
318  }
319 
320  mdb_env_close(db->env);
321  FREE(ptr);
322 }
323 
327 static const char *store_lmdb_version(void)
328 {
329  return "lmdb " MDB_VERSION_STRING;
330 }
331 
332 STORE_BACKEND_OPS(lmdb)
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
static void store_lmdb_close(void **ptr)
Implements StoreOps::close() -.
Definition: lmdb.c:302
static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
Implements StoreOps::delete_record() -.
Definition: lmdb.c:270
static void * store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
Implements StoreOps::fetch() -.
Definition: lmdb.c:189
static void store_lmdb_free(void *store, void **ptr)
Implements StoreOps::free() -.
Definition: lmdb.c:228
static void * store_lmdb_open(const char *path)
Implements StoreOps::open() -.
Definition: lmdb.c:131
static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
Implements StoreOps::store() -.
Definition: lmdb.c:236
static const char * store_lmdb_version(void)
Implements StoreOps::version() -.
Definition: lmdb.c:327
static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
Get an LMDB write transaction.
Definition: lmdb.c:106
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: logging.h:41
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:40
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