summaryrefslogtreecommitdiffstats
path: root/src/lookups/lmdb.c
blob: 4066751983d1c30e15059acf5d660c6089ce36e0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 2016 - 2018 */
/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */

#include "../exim.h"

#ifdef EXPERIMENTAL_LMDB

#include <lmdb.h>

typedef struct lmdbstrct
{
MDB_txn *txn;
MDB_dbi db_dbi;
} Lmdbstrct;


/*************************************************
*              Open entry point                  *
*************************************************/

static void *
lmdb_open(const uschar * filename, uschar ** errmsg)
{
MDB_env * db_env = NULL;
Lmdbstrct * lmdb_p;
int ret, save_errno;
const uschar * errstr;

lmdb_p = store_get(sizeof(Lmdbstrct), FALSE);
lmdb_p->txn = NULL;

if ((ret = mdb_env_create(&db_env)))
  {
  errstr = US"create environment";
  goto bad;
  }

if ((ret = mdb_env_open(db_env, CS filename, MDB_NOSUBDIR|MDB_RDONLY, 0660)))
  {
  errstr = string_sprintf("open environment with %s", filename);
  goto bad;
  }

if ((ret = mdb_txn_begin(db_env, NULL, MDB_RDONLY, &lmdb_p->txn)))
  {
  errstr = US"start transaction";
  goto bad;
  }

if ((ret = mdb_open(lmdb_p->txn, NULL, 0, &lmdb_p->db_dbi)))
  {
  errstr = US"open database";
  goto bad;
  }

return lmdb_p;

bad:
  save_errno = errno;
  if (lmdb_p->txn) mdb_txn_abort(lmdb_p->txn);
  if (db_env) mdb_env_close(db_env);
  *errmsg = string_sprintf("LMDB: Unable to %s: %s", errstr,  mdb_strerror(ret));
  errno = save_errno;
  return NULL;
}


/*************************************************
*              Find entry point                  *
*************************************************/

static int
lmdb_find(void * handle, const uschar * filename,
    const uschar * keystring, int length, uschar ** result, uschar ** errmsg,
    uint * do_cache, const uschar * opts)
{
int ret;
MDB_val dbkey, data;
Lmdbstrct * lmdb_p = handle;

dbkey.mv_data = CS keystring;
dbkey.mv_size = length;

DEBUG(D_lookup) debug_printf_indent("LMDB: lookup key: %s\n", CS keystring);

if ((ret = mdb_get(lmdb_p->txn, lmdb_p->db_dbi, &dbkey, &data)) == 0)
  {
  *result = string_copyn(US data.mv_data, data.mv_size);
  DEBUG(D_lookup) debug_printf_indent("LMDB: lookup result: %s\n", *result);
  return OK;
  }
else if (ret == MDB_NOTFOUND)
  {
  *errmsg = US"LMDB: lookup, no data found";
  DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
  return FAIL;
  }
else
  {
  *errmsg = string_sprintf("LMDB: lookup error: %s", mdb_strerror(ret));
  DEBUG(D_lookup) debug_printf_indent("%s\n", *errmsg);
  return DEFER;
  }
}


/*************************************************
*              Close entry point                 *
*************************************************/

static void
lmdb_close(void * handle)
{
Lmdbstrct * lmdb_p = handle;
MDB_env * db_env = mdb_txn_env(lmdb_p->txn);
mdb_txn_abort(lmdb_p->txn);
mdb_env_close(db_env);
}


/*************************************************
*         Version reporting entry point          *
*************************************************/

#include "../version.h"

void
lmdb_version_report(FILE * f)
{
fprintf(f, "Library version: LMDB: Compile: %d.%d.%d\n",
    MDB_VERSION_MAJOR, MDB_VERSION_MINOR, MDB_VERSION_PATCH);
#ifdef DYNLOOKUP
fprintf(f, "                        Exim version %s\n", EXIM_VERSION_STR);
#endif
}

static lookup_info lmdb_lookup_info = {
  .name = US"lmdb",			/* lookup name */
  .type = lookup_absfile,		/* query-style lookup */
  .open = lmdb_open,			/* open function */
  .check = NULL,			/* no check function */
  .find = lmdb_find,			/* find function */
  .close = lmdb_close,			/* close function */
  .tidy = NULL,				/* tidy function */
  .quote = NULL,			/* quoting function */
  .version_report = lmdb_version_report           /* version reporting */
};

#ifdef DYNLOOKUP
# define lmdb_lookup_module_info _lookup_module_info
#endif /* DYNLOOKUP */

static lookup_info *_lookup_list[] = { &lmdb_lookup_info };
lookup_module_info lmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };

#endif /* EXPERIMENTAL_LMDB */