This is an automated email from the ASF dual-hosted git repository.
twice pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 493c17f8 Add LISTLIB subcommand as an extension to FUNCTION (#1796)
493c17f8 is described below
commit 493c17f841c1673a137e95b5235dec2d51ca6ab4
Author: Twice <[email protected]>
AuthorDate: Mon Oct 9 17:28:45 2023 +0900
Add LISTLIB subcommand as an extension to FUNCTION (#1796)
Co-authored-by: hulk <[email protected]>
---
src/commands/cmd_function.cc | 4 +++
src/storage/scripting.cc | 46 ++++++++++++++++++++++++++++
src/storage/scripting.h | 1 +
tests/gocase/unit/scripting/function_test.go | 22 +++++++++++++
4 files changed, 73 insertions(+)
diff --git a/src/commands/cmd_function.cc b/src/commands/cmd_function.cc
index 8c38618e..2123cc72 100644
--- a/src/commands/cmd_function.cc
+++ b/src/commands/cmd_function.cc
@@ -61,6 +61,10 @@ struct CommandFunction : Commander {
}
return lua::FunctionListFunc(srv, funcname, output);
+ } else if (parser.EatEqICase("listlib")) {
+ auto libname = GET_OR_RET(parser.TakeStr().Prefixed("expect a library
name"));
+
+ return lua::FunctionListLib(srv, libname, output);
} else if (parser.EatEqICase("delete")) {
auto libname = GET_OR_RET(parser.TakeStr());
if (!lua::FunctionIsLibExist(conn, libname)) {
diff --git a/src/storage/scripting.cc b/src/storage/scripting.cc
index 5e0fa00e..b0d63eb5 100644
--- a/src/storage/scripting.cc
+++ b/src/storage/scripting.cc
@@ -383,6 +383,8 @@ bool FunctionIsLibExist(redis::Connection *conn, const
std::string &libname, boo
return static_cast<bool>(s);
}
+// FunctionCall will firstly find the function in the lua runtime,
+// if it is not found, it will try to load the library where the function is
located from storage
Status FunctionCall(redis::Connection *conn, const std::string &name, const
std::vector<std::string> &keys,
const std::vector<std::string> &argv, std::string *output,
bool read_only) {
auto srv = conn->GetServer();
@@ -422,6 +424,7 @@ Status FunctionCall(redis::Connection *conn, const
std::string &name, const std:
return Status::OK();
}
+// list all library names and their code (enabled via `with_code`)
Status FunctionList(Server *srv, const std::string &libname, bool with_code,
std::string *output) {
std::string start_key = engine::kLuaLibCodePrefix + libname;
std::string end_key = start_key;
@@ -455,6 +458,8 @@ Status FunctionList(Server *srv, const std::string
&libname, bool with_code, std
return Status::OK();
}
+// extension to Redis Function
+// list all function names and their corresponding library names
Status FunctionListFunc(Server *srv, const std::string &funcname, std::string
*output) {
std::string start_key = engine::kLuaFuncLibPrefix + funcname;
std::string end_key = start_key;
@@ -486,6 +491,47 @@ Status FunctionListFunc(Server *srv, const std::string
&funcname, std::string *o
return Status::OK();
}
+// extension to Redis Function
+// list detailed informantion of a specific library
+// NOTE: it is required to load the library to lua runtime before listing
(calling this function)
+// i.e. it will output nothing if the library is only in storage but not loaded
+Status FunctionListLib(Server *srv, const std::string &libname, std::string
*output) {
+ auto lua = srv->Lua();
+
+ lua_getglobal(lua, REDIS_FUNCTION_LIBRARIES);
+ if (lua_isnil(lua, -1)) {
+ lua_pop(lua, 1);
+ lua_newtable(lua);
+ }
+
+ lua_getfield(lua, -1, libname.c_str());
+ if (lua_isnil(lua, -1)) {
+ lua_pop(lua, 2);
+
+ return {Status::NotOK, "The library is not found or not loaded from
storage"};
+ }
+
+ output->append(redis::MultiLen(6));
+ output->append(redis::SimpleString("library_name"));
+ output->append(redis::SimpleString(libname));
+ output->append(redis::SimpleString("engine"));
+ output->append(redis::SimpleString("lua"));
+
+ auto count = lua_objlen(lua, -1);
+ output->append(redis::SimpleString("functions"));
+ output->append(redis::MultiLen(count));
+
+ for (size_t i = 1; i <= count; ++i) {
+ lua_rawgeti(lua, -1, static_cast<int>(i));
+ auto func = lua_tostring(lua, -1);
+ output->append(redis::SimpleString(func));
+ lua_pop(lua, 1);
+ }
+
+ lua_pop(lua, 2);
+ return Status::OK();
+}
+
Status FunctionDelete(Server *srv, const std::string &name) {
auto lua = srv->Lua();
diff --git a/src/storage/scripting.h b/src/storage/scripting.h
index 77df640e..4c72f930 100644
--- a/src/storage/scripting.h
+++ b/src/storage/scripting.h
@@ -68,6 +68,7 @@ Status FunctionCall(redis::Connection *conn, const
std::string &name, const std:
const std::vector<std::string> &argv, std::string *output,
bool read_only = false);
Status FunctionList(Server *srv, const std::string &libname, bool with_code,
std::string *output);
Status FunctionListFunc(Server *srv, const std::string &funcname, std::string
*output);
+Status FunctionListLib(Server *srv, const std::string &libname, std::string
*output);
Status FunctionDelete(Server *srv, const std::string &name);
bool FunctionIsLibExist(redis::Connection *conn, const std::string &libname,
bool need_check_storage = true,
bool read_only = false);
diff --git a/tests/gocase/unit/scripting/function_test.go
b/tests/gocase/unit/scripting/function_test.go
index 98c90ef2..3e262db2 100644
--- a/tests/gocase/unit/scripting/function_test.go
+++ b/tests/gocase/unit/scripting/function_test.go
@@ -160,4 +160,26 @@ func TestFunction(t *testing.T) {
util.ErrorRegexp(t, rdb.Do(ctx, "FCALL_RO", "myset", 1, "x",
3).Err(), ".*Write commands are not allowed.*")
})
+
+ t.Run("Restart server and test again", func(t *testing.T) {
+ srv.Restart()
+
+ require.Equal(t, rdb.Do(ctx, "FCALL", "myget", 1, "x").Val(),
"2")
+ require.Equal(t, rdb.Do(ctx, "FCALL", "hello", 0, "xxx").Val(),
"Hello, xxx!")
+
+ list := rdb.Do(ctx, "FUNCTION", "LIST").Val().([]interface{})
+ require.Equal(t, list[1].(string), "mylib1")
+ require.Equal(t, list[3].(string), "mylib3")
+ require.Equal(t, len(list), 4)
+ })
+
+ t.Run("FUNCTION LISTLIB", func(t *testing.T) {
+ list := rdb.Do(ctx, "FUNCTION", "LISTLIB",
"mylib1").Val().([]interface{})
+ require.Equal(t, list[1].(string), "mylib1")
+ require.Equal(t, list[5].([]interface{}),
[]interface{}{"hello", "reverse"})
+
+ list = rdb.Do(ctx, "FUNCTION", "LISTLIB",
"mylib3").Val().([]interface{})
+ require.Equal(t, list[1].(string), "mylib3")
+ require.Equal(t, list[5].([]interface{}),
[]interface{}{"myget", "myset"})
+ })
}