Skip to content

Commit d31283d

Browse files
committed
apr_dbm_lmdb: Add LMDB driver for apr_dbm.
git-svn-id: https://svn.apache.org/repos/asf/apr/apr-util/branches/1.7.x@1924405 13f79535-47bb-0310-9956-ffa450edef68
1 parent ad6fa6f commit d31283d

File tree

1 file changed

+376
-0
lines changed

1 file changed

+376
-0
lines changed

dbm/apr_dbm_lmdb.c

+376
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
/* Licensed to the Apache Software Foundation (ASF) under one or more
2+
* contributor license agreements. See the NOTICE file distributed with
3+
* this work for additional information regarding copyright ownership.
4+
* The ASF licenses this file to You under the Apache License, Version 2.0
5+
* (the "License"); you may not use this file except in compliance with
6+
* the License. You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "apr_strings.h"
18+
#define APR_WANT_MEMFUNC
19+
#include "apr_want.h"
20+
#include <stdio.h>
21+
22+
#if APR_HAVE_STDLIB_H
23+
#include <stdlib.h> /* for abort() */
24+
#endif
25+
26+
#include "apu.h"
27+
28+
#if APU_HAVE_LMDB
29+
30+
#include <lmdb.h>
31+
32+
#include "apr_dbm_private.h"
33+
34+
typedef struct {
35+
MDB_dbi dbi;
36+
MDB_cursor *cursor;
37+
MDB_txn *txn;
38+
MDB_env *env;
39+
} real_file_t;
40+
41+
42+
#define APR_DBM_LMDBMODE_RO MDB_RDONLY
43+
#define APR_DBM_LMDBMODE_RWCREATE MDB_CREATE
44+
#define APR_DBM_LMDBMODE_RW (MDB_RDONLY + MDB_CREATE + 1)
45+
#define APR_DBM_LMDBMODE_RWTRUNC (APR_DBM_LMDBMODE_RW + 1)
46+
47+
/* --------------------------------------------------------------------------
48+
**
49+
** UTILITY FUNCTIONS
50+
*/
51+
52+
/* Map a DB error to an apr_status_t */
53+
static apr_status_t db2s(int dberr)
54+
{
55+
/* MDB_* error codes are negative, which are mapped to EGENERAL;
56+
* positive error codes are errno which maps directly to
57+
* apr_status_t. MDB_ codes could be mapped to some status code
58+
* region. */
59+
return dberr < 0 ? APR_EGENERAL : dberr;
60+
}
61+
62+
/* Handle the return code of an mdb_* function (dberr), store the
63+
* error string for access via apr_dbm_geterror(), return translated
64+
* to an apr_status_t. */
65+
static apr_status_t set_error(apr_dbm_t *dbm, int dberr)
66+
{
67+
if ((dbm->errcode = dberr) == MDB_SUCCESS) {
68+
dbm->errmsg = NULL;
69+
}
70+
else {
71+
dbm->errmsg = mdb_strerror(dberr);
72+
}
73+
74+
return db2s(dberr);
75+
}
76+
77+
78+
/* --------------------------------------------------------------------------
79+
**
80+
** DEFINE THE VTABLE FUNCTIONS FOR LMDB
81+
**
82+
*/
83+
84+
#define DEFAULT_ENV_FLAGS (MDB_NOSUBDIR|MDB_NOSYNC|MDB_NOLOCK)
85+
86+
static apr_status_t vt_lmdb_open(apr_dbm_t **pdb, const char *pathname,
87+
apr_int32_t mode, apr_fileperms_t perm,
88+
apr_pool_t *pool)
89+
{
90+
real_file_t file;
91+
int dbi_open_flags = 0;
92+
int dbmode = 0;
93+
int truncate = 0;
94+
95+
*pdb = NULL;
96+
switch (mode) {
97+
case APR_DBM_READONLY:
98+
dbmode = APR_DBM_LMDBMODE_RO;
99+
break;
100+
case APR_DBM_READWRITE:
101+
dbmode = APR_DBM_LMDBMODE_RW;
102+
break;
103+
case APR_DBM_RWCREATE:
104+
dbi_open_flags = APR_DBM_LMDBMODE_RWCREATE;
105+
break;
106+
case APR_DBM_RWTRUNC:
107+
truncate = APR_DBM_LMDBMODE_RWTRUNC;
108+
break;
109+
default:
110+
return APR_EINVAL;
111+
}
112+
113+
{
114+
int dberr;
115+
file.txn = NULL;
116+
file.cursor = NULL;
117+
file.env = NULL;
118+
119+
dberr = mdb_env_create(&file.env);
120+
if (dberr == 0) {
121+
/* Default to 2GB map size which limits the total database
122+
* size to something reasonable. */
123+
dberr = mdb_env_set_mapsize(file.env, INT32_MAX);
124+
}
125+
126+
if (dberr == 0) {
127+
dberr = mdb_env_open(file.env, pathname, dbmode | DEFAULT_ENV_FLAGS, apr_posix_perms2mode(perm));
128+
}
129+
130+
if (dberr == 0) {
131+
dberr = mdb_txn_begin(file.env, NULL, dbmode, &file.txn);
132+
}
133+
134+
if (dberr == 0) {
135+
dberr = mdb_dbi_open(file.txn, NULL, dbi_open_flags, &file.dbi);
136+
137+
/* if mode == APR_DBM_RWTRUNC, drop database */
138+
if ((dberr == 0) && truncate) {
139+
dberr = mdb_drop(file.txn, file.dbi, 0);
140+
if (dberr != 0) {
141+
mdb_dbi_close(file.env, file.dbi);
142+
}
143+
}
144+
}
145+
146+
if (dberr != 0) {
147+
/* close the env handler */
148+
if (file.env)
149+
mdb_env_close(file.env);
150+
151+
return db2s(dberr);
152+
}
153+
}
154+
155+
/* we have an open database... return it */
156+
*pdb = apr_pcalloc(pool, sizeof(**pdb));
157+
(*pdb)->pool = pool;
158+
(*pdb)->type = &apr_dbm_type_lmdb;
159+
(*pdb)->file = apr_pmemdup(pool, &file, sizeof(file));
160+
161+
/* ### register a cleanup to close the DBM? */
162+
163+
return APR_SUCCESS;
164+
}
165+
166+
static void vt_lmdb_close(apr_dbm_t *dbm)
167+
{
168+
real_file_t *f = dbm->file;
169+
170+
/* try to commit all transactions that haven't been commited yet on close */
171+
if (f->txn) {
172+
mdb_txn_commit(f->txn);
173+
f->txn = NULL;
174+
f->cursor = NULL;
175+
}
176+
177+
if (f->cursor) {
178+
mdb_cursor_close(f->cursor);
179+
f->cursor = NULL;
180+
}
181+
182+
mdb_dbi_close(f->env, f->dbi);
183+
mdb_env_close(f->env);
184+
185+
f->env = NULL;
186+
f->dbi = 0;
187+
}
188+
189+
static apr_status_t vt_lmdb_fetch(apr_dbm_t *dbm, apr_datum_t key,
190+
apr_datum_t * pvalue)
191+
{
192+
real_file_t *f = dbm->file;
193+
MDB_val ckey = { 0 };
194+
MDB_val rd = { 0 };
195+
int dberr;
196+
197+
ckey.mv_data = key.dptr;
198+
ckey.mv_size = key.dsize;
199+
200+
dberr = mdb_get(f->txn, f->dbi, &(ckey), &(rd));
201+
202+
/* "not found" is not an error. return zero'd value. */
203+
if (dberr == MDB_NOTFOUND) {
204+
memset(&rd, 0, sizeof(rd));
205+
dberr = 0;
206+
}
207+
208+
pvalue->dptr = rd.mv_data;
209+
pvalue->dsize = rd.mv_size;
210+
211+
/* store the error info into DBM, and return a status code. Also, note
212+
that *pvalue should have been cleared on error. */
213+
return set_error(dbm, dberr);
214+
}
215+
216+
static apr_status_t vt_lmdb_store(apr_dbm_t *dbm, apr_datum_t key,
217+
apr_datum_t value)
218+
{
219+
real_file_t *f = dbm->file;
220+
int rv;
221+
MDB_val ckey = { 0 };
222+
MDB_val cvalue = { 0 };
223+
224+
ckey.mv_data = key.dptr;
225+
ckey.mv_size = key.dsize;
226+
227+
cvalue.mv_data = value.dptr;
228+
cvalue.mv_size = value.dsize;
229+
230+
if ((rv = mdb_put(f->txn, f->dbi, &ckey, &cvalue, 0)) == 0) {
231+
/* commit transaction */
232+
if ((rv = mdb_txn_commit(f->txn)) == MDB_SUCCESS) {
233+
f->cursor = NULL;
234+
rv = mdb_txn_begin(f->env, NULL, 0, &f->txn);
235+
}
236+
237+
/* if mdb_txn_commit OR mdb_txn_begin fails ... */
238+
if (rv != MDB_SUCCESS) {
239+
f->txn = NULL;
240+
}
241+
}
242+
243+
/* store any error info into DBM, and return a status code. */
244+
return set_error(dbm, rv);
245+
}
246+
247+
static apr_status_t vt_lmdb_del(apr_dbm_t *dbm, apr_datum_t key)
248+
{
249+
real_file_t *f = dbm->file;
250+
int rv;
251+
MDB_val ckey = { 0 };
252+
253+
ckey.mv_data = key.dptr;
254+
ckey.mv_size = key.dsize;
255+
256+
if ((rv = mdb_del(f->txn, f->dbi, &ckey, NULL)) == 0) {
257+
/* commit transaction */
258+
if ((rv = mdb_txn_commit(f->txn)) == MDB_SUCCESS) {
259+
f->cursor = NULL;
260+
rv = mdb_txn_begin(f->env, NULL, 0, &f->txn);
261+
}
262+
263+
/* if mdb_txn_commit OR mdb_txn_begin fails ... */
264+
if (rv != MDB_SUCCESS) {
265+
f->txn = NULL;
266+
}
267+
}
268+
269+
/* store any error info into DBM, and return a status code. */
270+
return set_error(dbm, rv);
271+
}
272+
273+
static int vt_lmdb_exists(apr_dbm_t *dbm, apr_datum_t key)
274+
{
275+
real_file_t *f = dbm->file;
276+
MDB_val ckey = { 0 }; /* converted key */
277+
MDB_val data = { 0 };
278+
int dberr;
279+
280+
ckey.mv_data = key.dptr;
281+
ckey.mv_size = key.dsize;
282+
283+
dberr = mdb_get(f->txn, f->dbi, &(ckey), &(data));
284+
285+
/* note: the result data is "loaned" to us; we don't need to free it */
286+
287+
/* DB returns DB_NOTFOUND if it doesn't exist. but we want to say
288+
that *any* error means it doesn't exist. */
289+
return dberr == 0;
290+
}
291+
292+
static apr_status_t vt_lmdb_firstkey(apr_dbm_t *dbm, apr_datum_t * pkey)
293+
{
294+
real_file_t *f = dbm->file;
295+
MDB_val first, data;
296+
int dberr;
297+
298+
if ((dberr = mdb_cursor_open(f->txn, f->dbi, &f->cursor)) == 0) {
299+
dberr = mdb_cursor_get(f->cursor, &first, &data, MDB_FIRST);
300+
if (dberr == MDB_NOTFOUND) {
301+
memset(&first, 0, sizeof(first));
302+
mdb_cursor_close(f->cursor);
303+
f->cursor = NULL;
304+
dberr = 0;
305+
}
306+
}
307+
else {
308+
/* clear first if mdb_cursor_open fails */
309+
memset(&first, 0, sizeof(first));
310+
}
311+
312+
pkey->dptr = first.mv_data;
313+
pkey->dsize = first.mv_size;
314+
315+
/* store any error info into DBM, and return a status code. */
316+
return set_error(dbm, dberr);
317+
}
318+
319+
static apr_status_t vt_lmdb_nextkey(apr_dbm_t *dbm, apr_datum_t * pkey)
320+
{
321+
real_file_t *f = dbm->file;
322+
MDB_val ckey, data;
323+
int dberr;
324+
325+
ckey.mv_data = pkey->dptr;
326+
ckey.mv_size = pkey->dsize;
327+
328+
if (f->cursor == NULL) {
329+
return APR_EINVAL;
330+
}
331+
332+
dberr = mdb_cursor_get(f->cursor, &ckey, &data, MDB_NEXT);
333+
if (dberr == MDB_NOTFOUND) {
334+
mdb_cursor_close(f->cursor);
335+
f->cursor = NULL;
336+
dberr = 0;
337+
ckey.mv_data = NULL;
338+
ckey.mv_size = 0;
339+
}
340+
341+
pkey->dptr = ckey.mv_data;
342+
pkey->dsize = ckey.mv_size;
343+
344+
/* store any error info into DBM, and return a status code. */
345+
return set_error(dbm, dberr);
346+
}
347+
348+
static void vt_lmdb_freedatum(apr_dbm_t *dbm, apr_datum_t data)
349+
{
350+
/* nothing to do */
351+
}
352+
353+
static void vt_lmdb_usednames(apr_pool_t *pool, const char *pathname,
354+
const char **used1, const char **used2)
355+
{
356+
*used1 = apr_pstrdup(pool, pathname);
357+
*used2 = apr_pstrcat(pool, pathname, "-lock", NULL);
358+
}
359+
360+
361+
APU_MODULE_DECLARE_DATA const apr_dbm_driver_t apr_dbm_type_lmdb = {
362+
"lmdb",
363+
364+
vt_lmdb_open,
365+
vt_lmdb_close,
366+
vt_lmdb_fetch,
367+
vt_lmdb_store,
368+
vt_lmdb_del,
369+
vt_lmdb_exists,
370+
vt_lmdb_firstkey,
371+
vt_lmdb_nextkey,
372+
vt_lmdb_freedatum,
373+
vt_lmdb_usednames
374+
};
375+
376+
#endif /* APU_HAVE_LMDB */

0 commit comments

Comments
 (0)