commit 490717dabe58fca268add95e367e0954477c9599 Author: Denny Lin Date: Mon Sep 21 15:21:03 2015 +0200 Add Kyoto Cabinet backend. diff --git a/configure.ac b/configure.ac index 933b365..c085e05 100644 --- a/configure.ac +++ b/configure.ac @@ -464,7 +464,7 @@ AC_CACHE_SAVE WITH_DB_ENGINE=db AC_ARG_WITH(database, AS_HELP_STRING([--with-database=ENGINE], - [choose database engine {db|qdbm|sqlite3|tokyocabinet} [[db]]]), + [choose database engine {db|qdbm|sqlite3|tokyocabinet|kyotocabinet} [[db]]]), [ WITH_DB_ENGINE=$withval ] ) @@ -503,6 +503,21 @@ case "x$WITH_DB_ENGINE" in ])],,AC_MSG_ERROR(Cannot link to tokyocabinet library.)) LIBS="$saveLIBS" ;; + xkyotocabinet) + AC_DEFINE(ENABLE_KYOTOCABINET_DATASTORE,1, [Enable kyotocabinet datastore]) + DB_TYPE=kyotocabinet + DB_EXT=.kct + AC_LIB_LINKFLAGS([kyotocabinet]) + LIBDB="$LIBKYOTOCABINET" + saveLIBS="$LIBS" + LIBS="$LIBS $LIBDB" + AC_LINK_IFELSE([AC_LANG_PROGRAM([ +#include + ], [ + KCDB *db = kcdbnew(); + ])],,AC_MSG_ERROR(Cannot link to kyotocabinet library.)) + LIBS="$saveLIBS" + ;; xqdbm) AC_DEFINE(ENABLE_QDBM_DATASTORE,1, [Enable qdbm datastore]) DB_TYPE=qdbm @@ -653,7 +668,7 @@ shared environments, you can use --disable-dbshared-test.])],true) LIBS="$saveLIBS" ;; *) - AC_MSG_ERROR([Invalid --with-database argument. Supported engines are db, qdbm, sqlite3, tokyocabinet.]) + AC_MSG_ERROR([Invalid --with-database argument. Supported engines are db, qdbm, sqlite3, tokyocabinet, kyotocabinet.]) ;; esac @@ -679,6 +694,7 @@ AC_SUBST(STATIC_DB) AM_CONDITIONAL(ENABLE_QDBM_DATASTORE, test "x$WITH_DB_ENGINE" = "xqdbm") AM_CONDITIONAL(ENABLE_SQLITE_DATASTORE, test "x$WITH_DB_ENGINE" = "xsqlite3") AM_CONDITIONAL(ENABLE_TOKYOCABINET_DATASTORE, test "x$WITH_DB_ENGINE" = "xtokyocabinet") +AM_CONDITIONAL(ENABLE_KYOTOCABINET_DATASTORE, test "x$WITH_DB_ENGINE" = "xkyotocabinet") dnl Use TRIO to replace missing snprintf/vsnprintf. needtrio=0 diff --git a/src/Makefile.am b/src/Makefile.am index 0b2f064..fbce226 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -190,6 +190,11 @@ datastore_SOURCE = datastore_tc.c \ datastore_opthelp_dummies.c \ datastore_dummies.c else +if ENABLE_KYOTOCABINET_DATASTORE +datastore_SOURCE = datastore_kc.c \ + datastore_opthelp_dummies.c \ + datastore_dummies.c +else if ENABLE_TRANSACTIONS datastore_SOURCE = datastore_db.c datastore_db_trans.c else @@ -203,6 +208,7 @@ endif endif endif endif +endif datastore_OBJECT = $(datastore_SOURCE:.c=.o) diff --git a/src/datastore_kc.c b/src/datastore_kc.c new file mode 100644 index 0000000..66a8f5e --- /dev/null +++ b/src/datastore_kc.c @@ -0,0 +1,315 @@ +/* $Id$ */ + +/***************************************************************************** + +NAME: +datastore_kc.c -- implements the datastore, using kyotocabinet. + +AUTHORS: +Gyepi Sam 2003 +Matthias Andree 2003 +Stefan Bellon 2003-2004 +Pierre Habouzit 2007 +Denny Lin 2015 + +******************************************************************************/ + +#include "common.h" + +#include +#include +#include +#include + +#include "datastore.h" +#include "datastore_db.h" +#include "error.h" +#include "paths.h" +#include "xmalloc.h" +#include "xstrdup.h" + +#define UNUSED(x) ((void)(x)) + +typedef struct { + char *name; + bool created; + bool writable; + KCDB *dbp; +} dbh_t; + +static int kc_txn_begin(void *vhandle) { + dbh_t *dbh = vhandle; + if (!dbh->writable || kcdbbegintran(dbh->dbp, false)) + return DST_OK; + print_error(__FILE__, __LINE__, "kcdbbegintran(%p), err: %d, %s", + dbh->dbp, + kcdbecode(dbh->dbp), kcdbemsg(dbh->dbp)); + return DST_FAILURE; +} + +static int kc_txn_abort(void *vhandle) { + dbh_t *dbh = vhandle; + if (!dbh->writable || kcdbendtran(dbh->dbp, false)) + return DST_OK; + print_error(__FILE__, __LINE__, "kcdbendtran(%p, false), err: %d, %s", + dbh->dbp, + kcdbecode(dbh->dbp), kcdbemsg(dbh->dbp)); + return DST_FAILURE; +} + +static int kc_txn_commit(void *vhandle) { + dbh_t *dbh = vhandle; + if (!dbh->writable || kcdbendtran(dbh->dbp, true)) + return DST_OK; + print_error(__FILE__, __LINE__, "kc_txn_commit(%p, true), err: %d, %s", + dbh->dbp, + kcdbecode(dbh->dbp), kcdbemsg(dbh->dbp)); + return DST_FAILURE; +} + +static dsm_t dsm_kc = { + /* public -- used in datastore.c */ + &kc_txn_begin, + &kc_txn_abort, + &kc_txn_commit, + /* private -- used in datastore_db_*.c */ + NULL, /* dsm_env_init */ + NULL, /* dsm_cleanup */ + NULL, /* dsm_cleanup_lite */ + NULL, /* dsm_get_env_dbe */ + NULL, /* dsm_database_name */ + NULL, /* dsm_recover_open */ + NULL, /* dsm_auto_commit_flags */ + NULL, /* dsm_get_rmw_flag */ + NULL, /* dsm_lock */ + NULL, /* dsm_common_close */ + NULL, /* dsm_sync */ + NULL, /* dsm_log_flush */ + NULL, /* dsm_pagesize */ + NULL, /* dsm_purgelogs */ + NULL, /* dsm_checkpoint */ + NULL, /* dsm_recover */ + NULL, /* dsm_remove */ + NULL, /* dsm_verify */ + NULL, /* dsm_list_logfiles */ + NULL /* dsm_leafpages */ +}; + +dsm_t *dsm = &dsm_kc; + +const char *db_version_str(void) +{ + static char v[80]; + if (v[0] == '\0') + snprintf(v, sizeof(v) - 1, "Kyoto Cabinet %s (TreeDB)", KCVERSION); + return v; +} + + +static dbh_t *dbh_init(bfpath *bfp) +{ + dbh_t *handle; + + handle = xmalloc(sizeof(dbh_t)); + memset(handle, 0, sizeof(dbh_t)); + + handle->name = xstrdup(bfp->filepath); + handle->created = false; + handle->writable = false; + handle->dbp = kcdbnew(); + + return handle; +} + + +static void dbh_free(dbh_t *handle) +{ + if (handle != NULL) { + xfree(handle->name); + kcdbdel(handle->dbp); + xfree(handle); + } +} + + +bool db_is_swapped(void *vhandle) +{ + UNUSED(vhandle); + + return false; +} + + +bool db_created(void *vhandle) +{ + dbh_t *handle = vhandle; + + return handle->created; +} + + +void *db_open(void *env, bfpath *bfp, dbmode_t open_mode) +{ + dbh_t *handle; + uint32_t mode; + bool ret; + + UNUSED(env); + + handle = dbh_init(bfp); + + handle->writable = open_mode & DS_WRITE; + mode = handle->writable ? KCOWRITER : KCOREADER; + ret = kcdbopen(handle->dbp, handle->name, mode); + if (!ret && handle->writable) { + ret = kcdbopen(handle->dbp, handle->name, mode | KCOCREATE); + handle->created = ret; + } + + if (!ret) + goto open_err; + + if (DEBUG_DATABASE(1)) + fprintf(dbgout, "kcdbopen(%s, %u)\n", handle->name, mode); + + return handle; + +open_err: + print_error(__FILE__, __LINE__, "kcdbopen(%s, %u), err: %d, %s", + handle->name, mode, + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); + dbh_free(handle); + + return NULL; +} + + +int db_delete(void *vhandle, const dbv_t *token) +{ + dbh_t *handle = vhandle; + bool ret; + + ret = kcdbremove(handle->dbp, token->data, token->leng); + if (!ret) { + print_error(__FILE__, __LINE__, "kcdbremove(\"%.*s\"), err: %d, %s", + CLAMP_INT_MAX(token->leng), (char *)token->data, + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); + exit(EX_ERROR); + } + + return 0; +} + + +int db_get_dbvalue(void *vhandle, const dbv_t *token, dbv_t *val) +{ + dbh_t *handle = vhandle; + char *data; + size_t dsiz; + + data = kcdbget(handle->dbp, token->data, token->leng, &dsiz); + if (data == NULL) + return DS_NOTFOUND; + + val->leng = min(val->leng, dsiz); + memcpy(val->data, data, val->leng); + kcfree(data); + + return 0; +} + +int db_set_dbvalue(void *vhandle, const dbv_t *token, const dbv_t *val) +{ + dbh_t *handle = vhandle; + bool ret; + + ret = kcdbset(handle->dbp, token->data, token->leng, val->data, val->leng); + if (!ret) { + print_error(__FILE__, __LINE__, + "kcdbset: (%.*s, %.*s), err: %d, %s", + CLAMP_INT_MAX(token->leng), (char *)token->data, + CLAMP_INT_MAX(val->leng), (char *)val->data, + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); + exit(EX_ERROR); + } + + return 0; +} + + +void db_close(void *vhandle) +{ + dbh_t *handle = vhandle; + + if (handle == NULL) + return; + + if (DEBUG_DATABASE(1)) + fprintf(dbgout, "kcdbclose: %s\n", handle->name); + + if (!kcdbclose(handle->dbp)) + print_error(__FILE__, __LINE__, "kcdbclose: %s, err: %d, %s", + handle->name, + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); + + dbh_free(handle); +} + + +void db_flush(void *vhandle) +{ + dbh_t *handle = vhandle; + + if (!kcdbsync(handle->dbp, false, NULL, NULL)) + print_error(__FILE__, __LINE__, "kcdbsync(), err: %d, %s", + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); +} + +ex_t db_foreach(void *vhandle, db_foreach_t hook, void *userdata) +{ + dbh_t *handle = vhandle; + KCCUR *cursor; + dbv_t dbv_key, dbv_data; + size_t ksiz, dsiz; + int ret; + ex_t retval = EX_OK; + char *key; + const char *data; + + cursor = kcdbcursor(handle->dbp); + if (!kccurjump(cursor)) { + print_error(__FILE__, __LINE__, "kccurjump(), err: %d, %s", + kcdbecode(handle->dbp), kcdbemsg(handle->dbp)); + retval = EX_ERROR; + goto done; + } + + while ((key = kccurget(cursor, &ksiz, &data, &dsiz, true)) != NULL) { + /* Copy to dbv_key and dbv_data */ + dbv_key.data = xstrdup(key); + dbv_key.leng = ksiz; + dbv_data.data = xstrdup(data); + dbv_data.leng = dsiz; + + /* Call function */ + ret = hook(&dbv_key, &dbv_data, userdata); + + xfree(dbv_key.data); + xfree(dbv_data.data); + kcfree(key); + + if (ret != 0) + break; + } + +done: + kccurdel(cursor); + + return retval; +} + +const char *db_str_err(int e) +{ + UNUSED(e); + return "unknown error"; +} diff --git a/src/tests/t.frame b/src/tests/t.frame index 355f6f2..7eaa221 100755 --- a/src/tests/t.frame +++ b/src/tests/t.frame @@ -48,6 +48,7 @@ case $DB_NAME in esac ;; *QDBM*) DB_TXN=false ;; *Tokyo*) DB_TXN=true ;; + *Kyoto*) DB_TXN=true ;; *SQLite*) DB_TXN=true ;; *TrivialDB*) DB_TXN=false ;; *) echo >&2 "Unknown data base type in bogofilter -V: $DB_NAME"