diff --git a/README.md b/README.md index 71b862e..28834df 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,26 @@ code, message, value = db:get("my_column_family", "key") -- Delete the key-value pair db:delete("my_column_family", "key") +-- Create a cursor for iterating over key-value pairs +code, message, cursor = db:cursor_init("my_column_family") +assert(code == 0, message) + +-- Move cursor to next key-value pair +code, message = cursor:next() +assert(code == 0, message) + +-- Get current key-value pair +code, message, value = cursor:get() +assert(code == 0, message) + +-- Move cursor to previous key-value pair +code, message = cursor:prev() +assert(code == 0, message) + +-- Free cursor when done +code, message = cursor:free() +assert(code == 0, message) + --- Close the database lib.close(db) -``` - -#### Test lua wrapper-library -```bash -cd build -lua ../test_lua.lua -``` +``` \ No newline at end of file diff --git a/spec/tidesdb_spec.lua b/spec/tidesdb_spec.lua index b594e60..d92d621 100644 --- a/spec/tidesdb_spec.lua +++ b/spec/tidesdb_spec.lua @@ -44,6 +44,17 @@ local value = "value" local value_size = string.len(value) local ttl = 10 +-- Setup test directory +os.execute("rm -rf " .. directory) +os.execute("mkdir -p " .. directory) +os.execute("chmod 777 " .. directory) + +describe("load", function() + it("should open tidesdb_lua", function() + assert.truthy(require("libtidesdb_lua")) + end) +end) + describe("open and close", function() it("should open and close db", function() assert.truthy(lib.open(directory)) @@ -233,3 +244,69 @@ describe("transactions put and delete", function() assert.is_equal(0, code) end) end) + +describe("cursor operations", function() + it("should perform cursor operations", function() + assert.truthy(lib.open(directory)) + + local code, message, db = lib.open(directory) + assert.is_equal(0, code) + + code, message = db:create_column_family(name, + threshold, + max_skip_list, + prob_skip_list, + enable_compression, + compression_algo, + enable_bloom_filter) + assert.is_equal(0, code) + + -- Insert test data + for i=1,5 do + code, message = db:put(name, "key" .. i, "value" .. i, ttl) + assert.is_equal(0, code) + end + + -- Create cursor + code, message, cursor = db:cursor_init(name) + assert.is_equal(0, code) + + -- Test next operations + for i=1,5 do + code, message, key, value = cursor:get() + assert.is_equal(0, code) + assert.is_equal(key, "key" .. i) + assert.is_equal(value, "value" .. i) + code, message = cursor:next() + if i ~= 5 then + assert.is_equal(0, code) + end + end + + -- Position cursor at last element + code, message = cursor:prev() + assert.is_equal(0, code) + + -- Test prev operations + for i=5,1,-1 do + code, message, key, value = cursor:get() + assert.is_equal(0, code) + assert.is_equal(key, "key" .. i) + assert.is_equal(value, "value" .. i) + code, message = cursor:prev() + if i ~= 1 then + assert.is_equal(0, code) + end + end + + -- Free cursor + code, message = cursor:free() + assert.is_equal(0, code) + + code, message = db:drop_column_family(name) + assert.is_equal(0, code) + + local code, message, db = lib.close(db) + assert.is_equal(0, code) + end) +end) diff --git a/src/tidesdb-lua.c b/src/tidesdb-lua.c index fe0ffbb..fc332fb 100644 --- a/src/tidesdb-lua.c +++ b/src/tidesdb-lua.c @@ -72,10 +72,14 @@ static int txn_commit(lua_State *L); static int txn_rollback(lua_State *L); static int txn_free(lua_State *L); +static int cursor_init(lua_State *L); +static int cursor_next(lua_State *L); +static int cursor_prev(lua_State *L); +static int cursor_get(lua_State *L); +static int cursor_free(lua_State *L); + static const luaL_Reg regs_tidesdb_lib_lua[] = { - {"open", db_open}, - {"close", db_close}, - {"txn_begin", txn_begin}, + {"open", db_open}, {"close", db_close}, {"txn_begin", txn_begin}, {"cursor_init", cursor_init}, {NULL, NULL}, }; @@ -88,6 +92,7 @@ static const luaL_Reg regs_tidesdb_lua[] = { {"compact_sstables", compact_sstables}, {"list_column_families", list_column_families}, {"txn_begin", txn_begin}, + {"cursor_init", cursor_init}, {NULL, NULL}, }; @@ -96,6 +101,11 @@ static const luaL_Reg regs_tidesdb_txn_lua[] = { {"rollback", txn_rollback}, {"free", txn_free}, {NULL, NULL}, }; +static const luaL_Reg regs_tidesdb_curs_lua[] = { + {"next", cursor_next}, {"prev", cursor_prev}, {"get", cursor_get}, + {"free", cursor_free}, {NULL, NULL}, +}; + static int db_open(lua_State *L) { const char *directory = luaL_checkstring(L, 1); @@ -293,6 +303,87 @@ static int txn_free(lua_State *L) LUA_RET_CODE() } +static int cursor_init(lua_State *L) +{ + lua_getfield(L, 1, "self_db"); + tidesdb_t *db = lua_touserdata(L, -1); + const char *column_family = luaL_checkstring(L, 2); + tidesdb_cursor_t *curs = NULL; + + tidesdb_err_t *ret = tidesdb_cursor_init(db, column_family, &curs); + if (ret) + { + lua_pushinteger(L, ret->code); + lua_pushstring(L, ret->message); + tidesdb_err_free(ret); + return 2; + } + else + { + lua_pushinteger(L, 0); + lua_pushstring(L, "OK"); + + lua_newtable(L); + luaL_setfuncs(L, regs_tidesdb_curs_lua, 0); + lua_pushlightuserdata(L, curs); + lua_setfield(L, -2, "self_curs"); + return 3; + } +} + +static int cursor_next(lua_State *L) +{ + lua_getfield(L, 1, "self_curs"); + tidesdb_cursor_t *curs = lua_touserdata(L, -1); + tidesdb_err_t *ret = tidesdb_cursor_next(curs); + LUA_RET_CODE() +} + +static int cursor_prev(lua_State *L) +{ + lua_getfield(L, 1, "self_curs"); + tidesdb_cursor_t *curs = lua_touserdata(L, -1); + tidesdb_err_t *ret = tidesdb_cursor_prev(curs); + LUA_RET_CODE() +} + +static int cursor_get(lua_State *L) +{ + lua_getfield(L, 1, "self_curs"); + tidesdb_cursor_t *curs = lua_touserdata(L, -1); + uint8_t *key = NULL; + size_t key_size = 0; + uint8_t *value = NULL; + size_t value_size = 0; + + tidesdb_err_t *ret = tidesdb_cursor_get(curs, &key, &key_size, &value, &value_size); + if (ret) + { + lua_pushinteger(L, ret->code); + lua_pushstring(L, ret->message); + tidesdb_err_free(ret); + return 2; + } + else + { + lua_pushinteger(L, 0); + lua_pushstring(L, "OK"); + lua_pushlstring(L, (char *)key, key_size); + lua_pushlstring(L, (char *)value, value_size); + free(key); + free(value); + return 4; + } +} + +static int cursor_free(lua_State *L) +{ + lua_getfield(L, 1, "self_curs"); + tidesdb_cursor_t *curs = lua_touserdata(L, -1); + tidesdb_err_t *ret = tidesdb_cursor_free(curs); + LUA_RET_CODE() +} + LUALIB_API int luaopen_libtidesdb_lua(lua_State *L) { luaL_newlib(L, regs_tidesdb_lib_lua);