NeoMutt  2021-02-05-666-ge300cd
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 "mutt/lib.h"
37 #include "lib.h"
38 
41 const size_t LMDB_DB_SIZE = 2147483648;
42 
47 {
51 };
52 
57 {
58  MDB_env *env;
59  MDB_txn *txn;
60  MDB_dbi db;
62 };
63 
69 static int mdb_get_r_txn(struct StoreLmdbCtx *ctx)
70 {
71  int rc;
72 
73  if (ctx->txn && ((ctx->txn_mode == TXN_READ) || (ctx->txn_mode == TXN_WRITE)))
74  return MDB_SUCCESS;
75 
76  if (ctx->txn)
77  rc = mdb_txn_renew(ctx->txn);
78  else
79  rc = mdb_txn_begin(ctx->env, NULL, MDB_RDONLY, &ctx->txn);
80 
81  if (rc == MDB_SUCCESS)
82  ctx->txn_mode = TXN_READ;
83  else
84  {
85  mutt_debug(LL_DEBUG2, "%s: %s\n",
86  ctx->txn ? "mdb_txn_renew" : "mdb_txn_begin", mdb_strerror(rc));
87  }
88 
89  return rc;
90 }
91 
97 static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
98 {
99  int rc;
100 
101  if (ctx->txn)
102  {
103  if (ctx->txn_mode == TXN_WRITE)
104  return MDB_SUCCESS;
105 
106  /* Free up the memory for readonly or reset transactions */
107  mdb_txn_abort(ctx->txn);
108  }
109 
110  rc = mdb_txn_begin(ctx->env, NULL, 0, &ctx->txn);
111  if (rc == MDB_SUCCESS)
112  ctx->txn_mode = TXN_WRITE;
113  else
114  mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
115 
116  return rc;
117 }
118 
122 static void *store_lmdb_open(const char *path)
123 {
124  if (!path)
125  return NULL;
126 
127  int rc;
128 
129  struct StoreLmdbCtx *ctx = mutt_mem_calloc(1, sizeof(struct StoreLmdbCtx));
130 
131  rc = mdb_env_create(&ctx->env);
132  if (rc != MDB_SUCCESS)
133  {
134  mutt_debug(LL_DEBUG2, "mdb_env_create: %s\n", mdb_strerror(rc));
135  FREE(&ctx);
136  return NULL;
137  }
138 
139  mdb_env_set_mapsize(ctx->env, LMDB_DB_SIZE);
140 
141  rc = mdb_env_open(ctx->env, path, MDB_NOSUBDIR, 0644);
142  if (rc != MDB_SUCCESS)
143  {
144  mutt_debug(LL_DEBUG2, "mdb_env_open: %s\n", mdb_strerror(rc));
145  goto fail_env;
146  }
147 
148  rc = mdb_get_r_txn(ctx);
149  if (rc != MDB_SUCCESS)
150  {
151  mutt_debug(LL_DEBUG2, "mdb_txn_begin: %s\n", mdb_strerror(rc));
152  goto fail_env;
153  }
154 
155  rc = mdb_dbi_open(ctx->txn, NULL, MDB_CREATE, &ctx->db);
156  if (rc != MDB_SUCCESS)
157  {
158  mutt_debug(LL_DEBUG2, "mdb_dbi_open: %s\n", mdb_strerror(rc));
159  goto fail_dbi;
160  }
161 
162  mdb_txn_reset(ctx->txn);
164  return ctx;
165 
166 fail_dbi:
167  mdb_txn_abort(ctx->txn);
169  ctx->txn = NULL;
170 
171 fail_env:
172  mdb_env_close(ctx->env);
173  FREE(&ctx);
174  return NULL;
175 }
176 
180 static void *store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
181 {
182  if (!store)
183  return NULL;
184 
185  MDB_val dkey;
186  MDB_val data;
187 
188  struct StoreLmdbCtx *ctx = store;
189 
190  dkey.mv_data = (void *) key;
191  dkey.mv_size = klen;
192  data.mv_data = NULL;
193  data.mv_size = 0;
194  int rc = mdb_get_r_txn(ctx);
195  if (rc != MDB_SUCCESS)
196  {
197  ctx->txn = NULL;
198  mutt_debug(LL_DEBUG2, "txn_renew: %s\n", mdb_strerror(rc));
199  return NULL;
200  }
201  rc = mdb_get(ctx->txn, ctx->db, &dkey, &data);
202  if (rc == MDB_NOTFOUND)
203  {
204  return NULL;
205  }
206  if (rc != MDB_SUCCESS)
207  {
208  mutt_debug(LL_DEBUG2, "mdb_get: %s\n", mdb_strerror(rc));
209  return NULL;
210  }
211 
212  *vlen = data.mv_size;
213  return data.mv_data;
214 }
215 
219 static void store_lmdb_free(void *store, void **ptr)
220 {
221  /* LMDB data is owned by the database */
222 }
223 
227 static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
228 {
229  if (!store)
230  return -1;
231 
232  MDB_val dkey;
233  MDB_val databuf;
234 
235  struct StoreLmdbCtx *ctx = store;
236 
237  dkey.mv_data = (void *) key;
238  dkey.mv_size = klen;
239  databuf.mv_data = value;
240  databuf.mv_size = vlen;
241  int rc = mdb_get_w_txn(ctx);
242  if (rc != MDB_SUCCESS)
243  {
244  mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
245  return rc;
246  }
247  rc = mdb_put(ctx->txn, ctx->db, &dkey, &databuf, 0);
248  if (rc != MDB_SUCCESS)
249  {
250  mutt_debug(LL_DEBUG2, "mdb_put: %s\n", mdb_strerror(rc));
251  mdb_txn_abort(ctx->txn);
253  ctx->txn = NULL;
254  }
255  return rc;
256 }
257 
261 static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
262 {
263  if (!store)
264  return -1;
265 
266  MDB_val dkey;
267 
268  struct StoreLmdbCtx *ctx = store;
269 
270  dkey.mv_data = (void *) key;
271  dkey.mv_size = klen;
272  int rc = mdb_get_w_txn(ctx);
273  if (rc != MDB_SUCCESS)
274  {
275  mutt_debug(LL_DEBUG2, "mdb_get_w_txn: %s\n", mdb_strerror(rc));
276  return rc;
277  }
278  rc = mdb_del(ctx->txn, ctx->db, &dkey, NULL);
279  if ((rc != MDB_SUCCESS) && (rc != MDB_NOTFOUND))
280  {
281  mutt_debug(LL_DEBUG2, "mdb_del: %s\n", mdb_strerror(rc));
282  mdb_txn_abort(ctx->txn);
284  ctx->txn = NULL;
285  }
286 
287  return rc;
288 }
289 
293 static void store_lmdb_close(void **ptr)
294 {
295  if (!ptr || !*ptr)
296  return;
297 
298  struct StoreLmdbCtx *db = *ptr;
299 
300  if (db->txn)
301  {
302  if (db->txn_mode == TXN_WRITE)
303  mdb_txn_commit(db->txn);
304  else
305  mdb_txn_abort(db->txn);
306 
308  db->txn = NULL;
309  }
310 
311  mdb_env_close(db->env);
312  FREE(ptr);
313 }
314 
318 static const char *store_lmdb_version(void)
319 {
320  return "lmdb " MDB_VERSION_STRING;
321 }
322 
323 STORE_BACKEND_OPS(lmdb)
static int mdb_get_w_txn(struct StoreLmdbCtx *ctx)
Get an LMDB write transaction.
Definition: lmdb.c:97
enum MdbTxnMode txn_mode
Definition: lmdb.c:61
MDB_env * env
Definition: lmdb.c:58
void * mutt_mem_calloc(size_t nmemb, size_t size)
Allocate zeroed memory on the heap.
Definition: memory.c:50
static void * store_lmdb_fetch(void *store, const char *key, size_t klen, size_t *vlen)
Implements StoreOps::fetch() -.
Definition: lmdb.c:180
static void store_lmdb_close(void **ptr)
Implements StoreOps::close() -.
Definition: lmdb.c:293
static void * store_lmdb_open(const char *path)
Implements StoreOps::open() -.
Definition: lmdb.c:122
#define STORE_BACKEND_OPS(_name)
Definition: lib.h:157
MDB_txn * txn
Definition: lmdb.c:59
LMDB context.
Definition: lmdb.c:56
Log at debug level 2.
Definition: logging.h:41
Write transaction in progress.
Definition: lmdb.c:50
static int mdb_get_r_txn(struct StoreLmdbCtx *ctx)
Get an LMDB read transaction.
Definition: lmdb.c:69
const size_t LMDB_DB_SIZE
The maximum size of the database file (2GiB).
Definition: lmdb.c:41
static int store_lmdb_store(void *store, const char *key, size_t klen, void *value, size_t vlen)
Implements StoreOps::store() -.
Definition: lmdb.c:227
MDB_dbi db
Definition: lmdb.c:60
Read transaction in progress.
Definition: lmdb.c:49
MdbTxnMode
LMDB transaction state.
Definition: lmdb.c:46
#define mutt_debug(LEVEL,...)
Definition: logging.h:85
static void store_lmdb_free(void *store, void **ptr)
Implements StoreOps::free() -.
Definition: lmdb.c:219
static int store_lmdb_delete_record(void *store, const char *key, size_t klen)
Implements StoreOps::delete_record() -.
Definition: lmdb.c:261
Transaction is uninitialised.
Definition: lmdb.c:48
#define FREE(x)
Definition: memory.h:40
static const char * store_lmdb_version(void)
Implements StoreOps::version() -.
Definition: lmdb.c:318
Convenience wrapper for the library headers.