You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kvrocks.apache.org by tw...@apache.org on 2023/10/09 02:28:27 UTC

[kvrocks] branch function-listlib created (now 84b9e3a1)

This is an automated email from the ASF dual-hosted git repository.

twice pushed a change to branch function-listlib
in repository https://gitbox.apache.org/repos/asf/kvrocks.git


      at 84b9e3a1 Add LISTLIB subcommand as an extension to FUNCTION

This branch includes the following new commits:

     new 84b9e3a1 Add LISTLIB subcommand as an extension to FUNCTION

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[kvrocks] 01/01: Add LISTLIB subcommand as an extension to FUNCTION

Posted by tw...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

twice pushed a commit to branch function-listlib
in repository https://gitbox.apache.org/repos/asf/kvrocks.git

commit 84b9e3a16883cb0413c16566cad22f5db3e76216
Author: PragmaTwice <tw...@apache.org>
AuthorDate: Mon Oct 9 11:28:10 2023 +0900

    Add LISTLIB subcommand as an extension to FUNCTION
---
 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"})
+	})
 }