lmdb-erl/c_src/emdb_drv.c

478 lines
11 KiB
C

/* -------------------------------------------------------------------------
* This file is part of EMDB - Erlang MDB API
*
* Copyright (c) 2012 by Aleph Archives. All rights reserved.
*
* -------------------------------------------------------------------------
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
* Public License.
*
* A copy of this license is available in the file LICENSE in the
* top-level directory of the distribution or, alternatively, at
* <http://www.OpenLDAP.org/license.html>.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
* -------------------------------------------------------------------------*/
/*
* C headers
*/
#include <sys/param.h> /* for MAXPATHLEN constant */
#include <erl_nif.h> /* for Erlang NIF interface */
#include "uthash.h" /* for uthash */
#include "mdb.h" /* for MDB interface */
#define FREE(p) (NULL == (p) ? 0 : (free(p), p = NULL))
#define FAIL_FAST(Error, Goto) \
do{ \
err = Error; \
goto Goto; \
}while(0)
struct emdb_map_t {
MDB_env * env;
MDB_dbi dbi;
UT_hash_handle hh;
};
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_none;
static struct emdb_map_t * emdb_map = NULL;
/* emdb ret */
#define EMDB_RET_KEY_EXIST "key_exist"
/* emdb errors */
#define EMDB_MALLOC_ERR "error_malloc"
#define EMDB_CREATE_ERR "error_create"
#define EMDB_MAPSIZE_ERR "error_mapsize"
#define EMDB_OPEN_ERR "error_open"
#define EMDB_TXN_BEGIN_ERR "error_txn_begin"
#define EMDB_TXN_COMMIT_ERR "error_txn_commit"
#define EMDB_OPEN_DBI_ERR "error_open_dbi"
#define EMDB_INVALID_HANDLE_ERR "error_invalid_handle"
#define EMDB_PUT_ERR "error_put"
#define EMDB_UPDATE_ERR "error_update"
#define EMDB_KEY_NOT_FOUND "error_key_not_found"
#define EMDB_DROP_ERR "error_drop"
/*
* Error handling callbacks
*/
static void emdb_free (struct emdb_map_t * emdb_obj)
{
FREE(emdb_obj);
}
/*
* Driver callbacks
*/
static ERL_NIF_TERM emdb_open_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
char dirname [MAXPATHLEN];
struct emdb_map_t * node;
MDB_txn * txn;
char * err;
ErlNifUInt64 mapsize;
ErlNifUInt64 envflags;
if (enif_get_string(env, argv[0], dirname, MAXPATHLEN, ERL_NIF_LATIN1) <= 0)
return enif_make_badarg(env);
if(! (node = calloc(1, sizeof(struct emdb_map_t))))
FAIL_FAST(EMDB_MALLOC_ERR, err3);
if (mdb_env_create(& (node -> env)))
FAIL_FAST(EMDB_CREATE_ERR, err2);
if (! enif_get_uint64(env, argv[1], & mapsize))
return enif_make_badarg(env);
if (mdb_env_set_mapsize(node -> env, mapsize))
FAIL_FAST(EMDB_MAPSIZE_ERR, err2);
if (! enif_get_uint64(env, argv[2], & envflags))
return enif_make_badarg(env);
if (mdb_env_open(node -> env, dirname, envflags, 0664))
FAIL_FAST(EMDB_OPEN_ERR, err2);
if (mdb_txn_begin(node -> env, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
if (mdb_open(txn, NULL, 0, & (node -> dbi)))
FAIL_FAST(EMDB_OPEN_DBI_ERR, err1);
if (mdb_txn_commit(txn))
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
HASH_ADD_PTR(emdb_map, env, node);
return enif_make_tuple(env, 2,
atom_ok,
enif_make_ulong(env, (unsigned long) node -> env));
err1:
mdb_txn_abort(txn);
err2:
mdb_env_close(node -> env);
err3:
emdb_free(node);
return enif_make_atom(env, err);
}
static ERL_NIF_TERM emdb_close_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
MDB_env * handle;
struct emdb_map_t * node;
unsigned long addr;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
HASH_DEL(emdb_map, node);
mdb_env_close(handle);
emdb_free(node);
return atom_ok;
}
static ERL_NIF_TERM emdb_put_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key;
ErlNifBinary val;
MDB_val mkey;
MDB_val mdata;
MDB_env * handle;
MDB_txn * txn;
struct emdb_map_t * node;
unsigned long addr;
char * err;
int ret;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
return enif_make_badarg(env);
if (! enif_inspect_iolist_as_binary(env, argv[2], &val))
return enif_make_badarg(env);
if (mdb_txn_begin(handle, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
mkey.mv_size = key.size;
mkey.mv_data = key.data;
mdata.mv_size = val.size;
mdata.mv_data = val.data;
ret = mdb_put(txn, node -> dbi, & mkey, & mdata, MDB_NOOVERWRITE);
if (MDB_KEYEXIST == ret)
FAIL_FAST(EMDB_RET_KEY_EXIST, err1);
if (ret)
FAIL_FAST(EMDB_PUT_ERR, err1);
if (mdb_txn_commit(txn))
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
return atom_ok;
err1:
mdb_txn_abort(txn);
err2:
return enif_make_atom(env, err);
}
static ERL_NIF_TERM emdb_get_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key;
ErlNifBinary val = {0};
ERL_NIF_TERM term;
MDB_val mkey;
MDB_val mdata;
MDB_env * handle;
MDB_txn * txn;
struct emdb_map_t * node;
char * err;
unsigned long addr;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
return enif_make_badarg(env);
mkey.mv_size = key.size;
mkey.mv_data = key.data;
if (mdb_txn_begin(handle, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err1);
if(mdb_get(txn, node -> dbi, & mkey, & mdata))
{
mdb_txn_abort(txn);
return atom_none;
}
val.size = mdata.mv_size;
val.data = mdata.mv_data;
term = enif_make_binary(env, &val);
mdb_txn_abort(txn);
return enif_make_tuple(env, 2,
atom_ok,
term);
err2:
mdb_txn_abort(txn);
err1:
return enif_make_atom(env, err);
}
static ERL_NIF_TERM emdb_del_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key;
MDB_val mkey;
MDB_env * handle;
MDB_txn * txn;
struct emdb_map_t * node;
char * err;
unsigned long addr;
int ret;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
return enif_make_badarg(env);
mkey.mv_size = key.size;
mkey.mv_data = key.data;
if (mdb_txn_begin(handle, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err);
ret = mdb_del(txn, node -> dbi, & mkey, NULL);
if (mdb_txn_commit(txn))
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err);
if(ret)
return atom_none;
return atom_ok;
err:
return enif_make_atom(env, err);
}
static ERL_NIF_TERM emdb_update_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
ErlNifBinary key;
ErlNifBinary val;
MDB_val mkey;
MDB_val mdata;
MDB_env * handle;
MDB_txn * txn;
struct emdb_map_t * node;
unsigned long addr;
char * err;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
if (! enif_inspect_iolist_as_binary(env, argv[1], &key))
return enif_make_badarg(env);
if (! enif_inspect_iolist_as_binary(env, argv[2], &val))
return enif_make_badarg(env);
if (mdb_txn_begin(handle, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
mkey.mv_size = key.size;
mkey.mv_data = key.data;
mdata.mv_size = val.size;
mdata.mv_data = val.data;
if (mdb_put(txn, node -> dbi, & mkey, & mdata, 0))
FAIL_FAST(EMDB_UPDATE_ERR, err1);
if (mdb_txn_commit(txn))
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
return atom_ok;
err1:
mdb_txn_abort(txn);
err2:
return enif_make_atom(env, err);
}
static ERL_NIF_TERM emdb_drop_nif (ErlNifEnv * env,
int argc, const ERL_NIF_TERM argv[])
{
MDB_env * handle;
MDB_txn * txn;
struct emdb_map_t * node;
unsigned long addr;
char * err;
int ret;
if (! enif_get_ulong(env, argv[0], & addr))
return enif_make_badarg(env);
handle = (MDB_env *) addr;
HASH_FIND_PTR(emdb_map, & handle, node);
if (NULL == node)
return enif_make_atom(env, EMDB_INVALID_HANDLE_ERR);
if (mdb_txn_begin(handle, NULL, 0, & txn))
FAIL_FAST(EMDB_TXN_BEGIN_ERR, err2);
ret = mdb_drop(txn, node -> dbi, 0);
if (ret)
FAIL_FAST(EMDB_DROP_ERR, err1);
if (mdb_txn_commit(txn))
FAIL_FAST(EMDB_TXN_COMMIT_ERR, err1);
return atom_ok;
err1:
mdb_txn_abort(txn);
err2:
return enif_make_atom(env, err);
}
static int emdb_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM load_info)
{
atom_ok = enif_make_atom(env, "ok");
atom_none = enif_make_atom(env, "none");
return (0);
}
static int emdb_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
return (0);
}
static int emdb_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM load_info)
{
return (0);
}
static void emdb_unload(ErlNifEnv* env, void* priv)
{
return;
}
static ErlNifFunc nif_funcs [] = {
{"open", 3, emdb_open_nif},
{"close", 1, emdb_close_nif},
{"put", 3, emdb_put_nif},
{"get", 2, emdb_get_nif},
{"del", 2, emdb_del_nif},
{"update", 3, emdb_update_nif},
{"drop", 1, emdb_drop_nif}
};
/* driver entry point */
ERL_NIF_INIT(emdb_drv,
nif_funcs,
& emdb_load,
& emdb_reload,
& emdb_upgrade,
& emdb_unload)