From 3df90b3764da1767c5227969d14a7264de7efe4d Mon Sep 17 00:00:00 2001 From: hulk Date: Mon, 22 Jan 2024 00:12:50 +0800 Subject: [PATCH] Add RESP3 big number (#2034) --- src/commands/cmd_server.cc | 4 +- src/server/redis_connection.h | 3 ++ src/storage/scripting.cc | 42 ++++++++++++++------- tests/gocase/unit/debug/debug_test.go | 4 ++ tests/gocase/unit/protocol/protocol_test.go | 2 + 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/commands/cmd_server.cc b/src/commands/cmd_server.cc index d34c55956c8..fb6ca8a44a2 100644 --- a/src/commands/cmd_server.cc +++ b/src/commands/cmd_server.cc @@ -623,6 +623,8 @@ class CommandDebug : public Commander { *output += redis::Integer(i); *output += conn->Bool(i == 1); } + } else if (protocol_type_ == "bignum") { + *output = conn->BigNumber("1234567999999999999999999999999999999"); } else if (protocol_type_ == "true") { *output = conn->Bool(true); } else if (protocol_type_ == "false") { @@ -631,7 +633,7 @@ class CommandDebug : public Commander { *output = conn->NilString(); } else { *output = redis::Error( - "Wrong protocol type name. Please use one of the following: string|int|array|set|true|false|null"); + "Wrong protocol type name. Please use one of the following: string|int|array|set|bignum|true|false|null"); } } else { return {Status::RedisInvalidCmd, "Unknown subcommand, should be DEBUG or PROTOCOL"}; diff --git a/src/server/redis_connection.h b/src/server/redis_connection.h index 7f622fe59de..47b694a0fb1 100644 --- a/src/server/redis_connection.h +++ b/src/server/redis_connection.h @@ -65,6 +65,9 @@ class Connection : public EvbufCallbackBase { RESP GetProtocolVersion() const { return protocol_version_; } void SetProtocolVersion(RESP version) { protocol_version_ = version; } std::string Bool(bool b) const; + std::string BigNumber(const std::string &n) const { + return protocol_version_ == RESP::v3 ? "(" + n + CRLF : BulkString(n); + } std::string NilString() const { return redis::NilString(protocol_version_); } std::string NilArray() const { return protocol_version_ == RESP::v3 ? "_" CRLF : "*-1" CRLF; } std::string MultiBulkString(const std::vector &values, bool output_nil_for_empty_string = true) const; diff --git a/src/storage/scripting.cc b/src/storage/scripting.cc index 4d94cd4ac6f..8105d321464 100644 --- a/src/storage/scripting.cc +++ b/src/storage/scripting.cc @@ -1080,6 +1080,7 @@ std::string ReplyToRedisReply(redis::Connection *conn, lua_State *lua) { std::string output; const char *obj_s = nullptr; size_t obj_len = 0; + int j = 0, mbulklen = 0; int t = lua_type(lua, -1); switch (t) { @@ -1113,6 +1114,7 @@ std::string ReplyToRedisReply(redis::Connection *conn, lua_State *lua) { return output; } lua_pop(lua, 1); /* Discard field name pushed before. */ + /* Handle status reply. */ lua_pushstring(lua, "ok"); lua_gettable(lua, -2); @@ -1122,23 +1124,35 @@ std::string ReplyToRedisReply(redis::Connection *conn, lua_State *lua) { output = redis::BulkString(std::string(obj_s, obj_len)); lua_pop(lua, 1); return output; - } else { - int j = 1, mbulklen = 0; - lua_pop(lua, 1); /* Discard the 'ok' field value we popped */ - while (true) { - lua_pushnumber(lua, j++); - lua_gettable(lua, -2); - t = lua_type(lua, -1); - if (t == LUA_TNIL) { - lua_pop(lua, 1); - break; - } - mbulklen++; - output += ReplyToRedisReply(conn, lua); + } + lua_pop(lua, 1); /* Discard the 'ok' field value we pushed */ + + /* Handle big number reply. */ + lua_pushstring(lua, "big_number"); + lua_gettable(lua, -2); + t = lua_type(lua, -1); + if (t == LUA_TSTRING) { + obj_s = lua_tolstring(lua, -1, &obj_len); + output = conn->BigNumber(std::string(obj_s, obj_len)); + lua_pop(lua, 1); + return output; + } + lua_pop(lua, 1); /* Discard the 'big_number' field value we pushed */ + + j = 1, mbulklen = 0; + while (true) { + lua_pushnumber(lua, j++); + lua_gettable(lua, -2); + t = lua_type(lua, -1); + if (t == LUA_TNIL) { lua_pop(lua, 1); + break; } - output = redis::MultiLen(mbulklen) + output; + mbulklen++; + output += ReplyToRedisReply(conn, lua); + lua_pop(lua, 1); } + output = redis::MultiLen(mbulklen) + output; break; default: output = conn->NilString(); diff --git a/tests/gocase/unit/debug/debug_test.go b/tests/gocase/unit/debug/debug_test.go index faa2981141d..1aae088f0da 100644 --- a/tests/gocase/unit/debug/debug_test.go +++ b/tests/gocase/unit/debug/debug_test.go @@ -21,6 +21,7 @@ package debug import ( "context" + "math/big" "testing" "github.com/redis/go-redis/v9" @@ -46,6 +47,7 @@ func TestDebugProtocolV2(t *testing.T) { "array": []interface{}{int64(0), int64(1), int64(2)}, "set": []interface{}{int64(0), int64(1), int64(2)}, "map": []interface{}{int64(0), int64(0), int64(1), int64(1), int64(2), int64(0)}, + "bignum": "1234567999999999999999999999999999999", "true": int64(1), "false": int64(0), } @@ -83,12 +85,14 @@ func TestDebugProtocolV3(t *testing.T) { defer func() { require.NoError(t, rdb.Close()) }() t.Run("debug protocol type", func(t *testing.T) { + bignum, _ := big.NewInt(0).SetString("1234567999999999999999999999999999999", 10) types := map[string]interface{}{ "string": "Hello World", "integer": int64(12345), "array": []interface{}{int64(0), int64(1), int64(2)}, "set": []interface{}{int64(0), int64(1), int64(2)}, "map": map[interface{}]interface{}{int64(0): false, int64(1): true, int64(2): false}, + "bignum": bignum, "true": true, "false": false, } diff --git a/tests/gocase/unit/protocol/protocol_test.go b/tests/gocase/unit/protocol/protocol_test.go index 7919bf77f86..33202c90078 100644 --- a/tests/gocase/unit/protocol/protocol_test.go +++ b/tests/gocase/unit/protocol/protocol_test.go @@ -156,6 +156,7 @@ func TestProtocolRESP2(t *testing.T) { "array": {"*3", ":0", ":1", ":2"}, "set": {"*3", ":0", ":1", ":2"}, "map": {"*6", ":0", ":0", ":1", ":1", ":2", ":0"}, + "bignum": {"$37", "1234567999999999999999999999999999999"}, "true": {":1"}, "false": {":0"}, "null": {"$-1"}, @@ -210,6 +211,7 @@ func TestProtocolRESP3(t *testing.T) { "array": {"*3", ":0", ":1", ":2"}, "set": {"~3", ":0", ":1", ":2"}, "map": {"%3", ":0", "#f", ":1", "#t", ":2", "#f"}, + "bignum": {"(1234567999999999999999999999999999999"}, "true": {"#t"}, "false": {"#f"}, "null": {"_"},