Skip to content
Snippets Groups Projects
Commit 2edf603a authored by Daniel Salzman's avatar Daniel Salzman
Browse files

Merge branch 'namedb_iter_seek' into 'master'

Namedb iter seek

This merge request extends the namedb api for following operations:
- `clear()` which equals to deletion of all entries in the database
- `iter_seek()` which returns an iterator to given key (or first, last, leq, geq)
  - this is not supported in the trie yet, but it works for lmdb backend
The unittests are extended to cover these operations.

See merge request !302
parents e8056213 a9760a74
Branches
Tags
No related merge requests found
......@@ -21,8 +21,20 @@
#include "common/mempattern.h"
enum {
KNOT_NAMEDB_RDONLY = 1 << 0,
KNOT_NAMEDB_SORTED = 1 << 1
/* Database flags */
KNOT_NAMEDB_RDONLY = 1 << 0, /*!< Read only. */
KNOT_NAMEDB_SORTED = 1 << 1, /*!< Sorted output. */
/* Operations */
KNOT_NAMEDB_NOOP = 1 << 2, /*!< No operation. */
KNOT_NAMEDB_FIRST = 1 << 3, /*!< First entry. */
KNOT_NAMEDB_LAST = 1 << 4, /*!< Last entry. */
KNOT_NAMEDB_NEXT = 1 << 5, /*!< Next entry. */
KNOT_NAMEDB_PREV = 1 << 6, /*!< Previous entry. */
KNOT_NAMEDB_LEQ = 1 << 7, /*!< Lesser or equal. */
KNOT_NAMEDB_GEQ = 1 << 8 /*!< Greater or equal. */
};
typedef void knot_namedb_t;
......@@ -56,13 +68,15 @@ struct namedb_api {
/* Data access */
int (*count)(knot_txn_t *txn);
int (*clear)(knot_txn_t *txn);
int (*find)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags);
int (*insert)(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags);
int (*del)(knot_txn_t *txn,knot_val_t *key);
int (*del)(knot_txn_t *txn, knot_val_t *key);
/* Iteration */
knot_iter_t *(*iter_begin)(knot_txn_t *txn, unsigned flags);
knot_iter_t *(*iter_seek)(knot_iter_t *iter, knot_val_t *key, unsigned flags);
knot_iter_t *(*iter_next)(knot_iter_t *iter);
int (*iter_key)(knot_iter_t *iter, knot_val_t *key);
int (*iter_val)(knot_iter_t *iter, knot_val_t *val);
......
......@@ -16,9 +16,10 @@
#ifdef HAVE_LMDB
#include <lmdb.h>
#include <assert.h>
#include <sys/stat.h>
#include <unistd.h>
#include <lmdb.h>
#include "common/namedb/namedb_lmdb.h"
#include "libknot/errcode.h"
......@@ -66,7 +67,7 @@ static int lmdb_error_to_knot(int error)
static int dbase_open(struct lmdb_env *env, const char *path)
{
int ret = mdb_env_create(&env->env);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
......@@ -78,7 +79,7 @@ static int dbase_open(struct lmdb_env *env, const char *path)
size_t map_size = (LMDB_MAPSIZE / page_size) * page_size;
ret = mdb_env_set_mapsize(env->env, map_size);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
mdb_env_close(env->env);
return lmdb_error_to_knot(ret);
}
......@@ -90,32 +91,32 @@ static int dbase_open(struct lmdb_env *env, const char *path)
}
ret = mdb_env_open(env->env, path, 0, LMDB_FILE_MODE);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
mdb_env_close(env->env);
return lmdb_error_to_knot(ret);
}
MDB_txn *txn = NULL;
ret = mdb_txn_begin(env->env, NULL, 0, &txn);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
mdb_env_close(env->env);
return lmdb_error_to_knot(ret);
}
ret = mdb_open(txn, NULL, 0, &env->dbi);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
mdb_txn_abort(txn);
mdb_env_close(env->env);
return lmdb_error_to_knot(ret);
}
ret = mdb_txn_commit(txn);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
mdb_env_close(env->env);
return lmdb_error_to_knot(ret);
}
return 0;
return KNOT_EOK;
}
static void dbase_close(struct lmdb_env *env)
......@@ -133,7 +134,7 @@ static int init(const char *config, knot_namedb_t **db_ptr, mm_ctx_t *mm)
memset(env, 0, sizeof(struct lmdb_env));
int ret = dbase_open(env, config);
if (ret != 0) {
if (ret != KNOT_EOK) {
mm_free(mm, env);
return ret;
}
......@@ -166,7 +167,7 @@ static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
struct lmdb_env *env = db;
int ret = mdb_txn_begin(env->env, NULL, txn_flags, (MDB_txn **)&txn->txn);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
......@@ -176,7 +177,7 @@ static int txn_begin(knot_namedb_t *db, knot_txn_t *txn, unsigned flags)
static int txn_commit(knot_txn_t *txn)
{
int ret = mdb_txn_commit((MDB_txn *)txn->txn);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
......@@ -194,83 +195,67 @@ static int count(knot_txn_t *txn)
MDB_stat stat;
int ret = mdb_stat(txn->txn, env->dbi, &stat);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
return stat.ms_entries;
}
static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
static int clear(knot_txn_t *txn)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = { key->len, key->data };
MDB_val data = { 0, NULL };
int ret = mdb_get(txn->txn, env->dbi, &db_key, &data);
if (ret != 0) {
if (ret == MDB_NOTFOUND) {
return KNOT_ENOENT;
} else {
return lmdb_error_to_knot(ret);
}
int ret = mdb_drop(txn->txn, env->dbi, 0);
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
val->data = data.mv_data;
val->len = data.mv_size;
return KNOT_EOK;
}
static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
static knot_iter_t *iter_set(knot_iter_t *iter, knot_val_t *key, unsigned flags)
{
struct lmdb_env *env = txn->db;
MDB_cursor *cursor = NULL;
int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor);
if (ret != 0) {
return lmdb_error_to_knot(ret);
MDB_cursor *cursor = iter;
MDB_cursor_op op = MDB_SET;
switch(flags) {
case KNOT_NAMEDB_NOOP: return cursor;
case KNOT_NAMEDB_FIRST: op = MDB_FIRST; break;
case KNOT_NAMEDB_LAST: op = MDB_LAST; break;
case KNOT_NAMEDB_NEXT: op = MDB_NEXT; break;
case KNOT_NAMEDB_PREV: op = MDB_PREV; break;
case KNOT_NAMEDB_LEQ:
case KNOT_NAMEDB_GEQ: op = MDB_SET_RANGE; break;
default: break;
}
MDB_val db_key = { key->len, key->data };
MDB_val data = { val->len, val->data };
ret = mdb_cursor_get(cursor, &db_key, NULL, MDB_SET);
if (ret != 0) {
mdb_cursor_close(cursor);
if (ret == MDB_NOTFOUND) {
// Insert new item
ret = mdb_put(txn->txn, env->dbi, &db_key, &data, 0);
if (ret != 0) {
return lmdb_error_to_knot(ret);
}
return KNOT_EOK;
} else {
return ret;
}
MDB_val db_key = { 0, NULL };
if (key) {
db_key.mv_data = key->data;
db_key.mv_size = key->len;
}
ret = mdb_cursor_put(cursor, &db_key, &data, MDB_CURRENT);
mdb_cursor_close(cursor);
if (ret != 0) {
return lmdb_error_to_knot(ret);
int ret = mdb_cursor_get(cursor, key ? &db_key : NULL, NULL, op);
/* LEQ is not supported in LMDB, workaround using GEQ. */
if (flags == KNOT_NAMEDB_LEQ && key) {
/* Searched key is after the last key. */
if (ret != MDB_SUCCESS) {
return iter_set(iter, NULL, KNOT_NAMEDB_LAST);
}
/* If the searched key != matched, get previous. */
if ((key->len != db_key.mv_size) || (memcmp(key->data, db_key.mv_data, key->len) != 0)) {
return iter_set(iter, NULL, KNOT_NAMEDB_PREV);
}
}
return KNOT_EOK;
}
static int del(knot_txn_t *txn, knot_val_t *key)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = { key->len, key->data };
MDB_val data = { 0, NULL };
int ret = mdb_del(txn->txn, env->dbi, &db_key, &data);
if (ret != 0) {
return lmdb_error_to_knot(ret);
if (ret != MDB_SUCCESS) {
mdb_cursor_close(cursor);
return NULL;
}
return KNOT_EOK;
return cursor;
}
static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
......@@ -279,30 +264,19 @@ static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
MDB_cursor *cursor = NULL;
int ret = mdb_cursor_open(txn->txn, env->dbi, &cursor);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return NULL;
}
ret = mdb_cursor_get(cursor, NULL, NULL, MDB_FIRST);
if (ret != 0) {
mdb_cursor_close(cursor);
return NULL;
}
return cursor;
/* Clear sorted flag, as it's always sorted. */
flags &= ~KNOT_NAMEDB_SORTED;
return iter_set(cursor, NULL, (flags == 0) ? KNOT_NAMEDB_FIRST : flags);
}
static knot_iter_t *iter_next(knot_iter_t *iter)
{
MDB_cursor *cursor = iter;
int ret = mdb_cursor_get(cursor, NULL, NULL, MDB_NEXT);
if (ret != 0) {
mdb_cursor_close(cursor);
return NULL;
}
return cursor;
return iter_set(iter, NULL, KNOT_NAMEDB_NEXT);
}
static int iter_key(knot_iter_t *iter, knot_val_t *key)
......@@ -311,7 +285,7 @@ static int iter_key(knot_iter_t *iter, knot_val_t *key)
MDB_val mdb_key, mdb_val;
int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
......@@ -326,7 +300,7 @@ static int iter_val(knot_iter_t *iter, knot_val_t *val)
MDB_val mdb_key, mdb_val;
int ret = mdb_cursor_get(cursor, &mdb_key, &mdb_val, MDB_GET_CURRENT);
if (ret != 0) {
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
......@@ -345,14 +319,62 @@ static void iter_finish(knot_iter_t *iter)
mdb_cursor_close(cursor);
}
static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
knot_iter_t *iter = iter_begin(txn, KNOT_NAMEDB_NOOP);
if (iter == NULL) {
return KNOT_ERROR;
}
int ret = KNOT_EOK;
if (iter_set(iter, key, flags) == NULL) {
return KNOT_ENOENT;
} else {
ret = iter_val(iter, val);
}
iter_finish(iter);
return ret;
}
static int insert(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = { key->len, key->data };
MDB_val data = { val->len, val->data };
int ret = mdb_put(txn->txn, env->dbi, &db_key, &data, 0);
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
return KNOT_EOK;
}
static int del(knot_txn_t *txn, knot_val_t *key)
{
struct lmdb_env *env = txn->db;
MDB_val db_key = { key->len, key->data };
MDB_val data = { 0, NULL };
int ret = mdb_del(txn->txn, env->dbi, &db_key, &data);
if (ret != MDB_SUCCESS) {
return lmdb_error_to_knot(ret);
}
return KNOT_EOK;
}
const struct namedb_api *namedb_lmdb_api(void)
{
static const struct namedb_api api = {
"lmdb",
init, deinit,
txn_begin, txn_commit, txn_abort,
count, find, insert, del,
iter_begin, iter_next, iter_key, iter_val, iter_finish
count, clear, find, insert, del,
iter_begin, iter_set, iter_next, iter_key, iter_val, iter_finish
};
return &api;
......
......@@ -14,6 +14,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include "libknot/errcode.h"
#include "common/namedb/namedb_trie.h"
......@@ -68,6 +70,13 @@ static int count(knot_txn_t *txn)
return hattrie_weight((hattrie_t *)txn->db);
}
static int clear(knot_txn_t *txn)
{
hattrie_clear((hattrie_t *)txn->db);
return KNOT_EOK;
}
static int find(knot_txn_t *txn, knot_val_t *key, knot_val_t *val, unsigned flags)
{
value_t *ret = hattrie_tryget((hattrie_t *)txn->db, key->data, key->len);
......@@ -98,7 +107,21 @@ static int del(knot_txn_t *txn, knot_val_t *key)
static knot_iter_t *iter_begin(knot_txn_t *txn, unsigned flags)
{
return hattrie_iter_begin((hattrie_t *)txn->db, (flags & KNOT_NAMEDB_SORTED));
bool is_sorted = (flags & KNOT_NAMEDB_SORTED);
flags &= ~KNOT_NAMEDB_SORTED;
/* No operations other than begin are supported right now. */
if (flags != 0) {
return NULL;
}
return hattrie_iter_begin((hattrie_t *)txn->db, is_sorted);
}
static knot_iter_t *iter_seek(knot_iter_t *iter, knot_val_t *key, unsigned flags)
{
assert(0);
return NULL; /* ENOTSUP */
}
static knot_iter_t *iter_next(knot_iter_t *iter)
......@@ -145,8 +168,8 @@ const struct namedb_api *namedb_trie_api(void)
"hattrie",
init, deinit,
txn_begin, txn_commit, txn_abort,
count, find, insert, del,
iter_begin, iter_next, iter_key, iter_val, iter_finish
count, clear, find, insert, del,
iter_begin, iter_seek, iter_next, iter_key, iter_val, iter_finish
};
return &api;
......
......@@ -17,6 +17,7 @@
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include <tap/basic.h>
#include "libknot/errcode.h"
......@@ -124,6 +125,9 @@ static void namedb_test_set(unsigned nkeys, char **keys, char *dbid,
is_int(db_size, iterated, "%s: unsorted iteration", api->name);
/* Sorted iteration. */
char first_key[KEY_MAXLEN] = { '\0' };
char second_key[KEY_MAXLEN] = { '\0' };
char last_key[KEY_MAXLEN] = { '\0' };
char key_buf[KEY_MAXLEN] = {'\0'};
iterated = 0;
it = api->iter_begin(&txn, KNOT_NAMEDB_SORTED);
......@@ -135,15 +139,68 @@ static void namedb_test_set(unsigned nkeys, char **keys, char *dbid,
api->name, key_buf, (const char *)key.data);
break;
}
if (iterated == 1) {
memcpy(second_key, key.data, key.len);
}
} else {
memcpy(first_key, key.data, key.len);
}
++iterated;
memcpy(key_buf, key.data, key.len);
it = api->iter_next(it);
}
is_int(db_size, iterated, "hattrie: sorted iteration");
strcpy(last_key, key_buf);
is_int(db_size, iterated, "%s: sorted iteration", api->name);
api->iter_finish(it);
/* Interactive iteration. */
it = api->iter_begin(&txn, KNOT_NAMEDB_NOOP);
if (it != NULL) { /* If supported. */
/* Check if first and last keys are reachable */
it = api->iter_seek(it, NULL, KNOT_NAMEDB_FIRST);
ret = api->iter_key(it, &key);
is_string(first_key, key.data, "%s: iter_set(FIRST)", api->name);
/* Check left/right iteration. */
it = api->iter_seek(it, &key, KNOT_NAMEDB_NEXT);
ret = api->iter_key(it, &key);
is_string(second_key, key.data, "%s: iter_set(NEXT)", api->name);
it = api->iter_seek(it, &key, KNOT_NAMEDB_PREV);
ret = api->iter_key(it, &key);
is_string(first_key, key.data, "%s: iter_set(PREV)", api->name);
it = api->iter_seek(it, &key, KNOT_NAMEDB_LAST);
ret = api->iter_key(it, &key);
is_string(last_key, key.data, "%s: iter_set(LAST)", api->name);
/* Check if prev(last_key + 1) is the last_key */
strcpy(key_buf, last_key);
key_buf[0] += 1;
KEY_SET(key, key_buf);
it = api->iter_seek(it, &key, KNOT_NAMEDB_LEQ);
ret = api->iter_key(it, &key);
is_string(last_key, key.data, "%s: iter_set(LEQ)", api->name);
/* Check if next(first_key - 1) is the first_key */
strcpy(key_buf, first_key);
key_buf[0] -= 1;
KEY_SET(key, key_buf);
it = api->iter_seek(it, &key, KNOT_NAMEDB_GEQ);
ret = api->iter_key(it, &key);
is_string(first_key, key.data, "%s: iter_set(GEQ)", api->name);
api->iter_finish(it);
}
api->txn_abort(&txn);
/* Clear database and recheck. */
ret = api->txn_begin(db, &txn, 0);
ret = api->clear(&txn);
is_int(0, ret, "%s: clear()", api->name);
ret = api->txn_commit(&txn);
is_int(0, ret, "%s: commit clear", api->name);
/* Check if the database is empty. */
ret = api->txn_begin(db, &txn, KNOT_NAMEDB_RDONLY);
db_size = api->count(&txn);
is_int(0, db_size, "%s: count after clear = %d", api->name, db_size);
api->txn_abort(&txn);
api->deinit(db);
}
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment