You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kvrocks.apache.org by hu...@apache.org on 2023/06/15 03:13:37 UTC

[incubator-kvrocks] branch unstable updated: Fix ZRANGE command should return an empty array when count = 0 (#1492)

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

hulk pushed a commit to branch unstable
in repository https://gitbox.apache.org/repos/asf/incubator-kvrocks.git


The following commit(s) were added to refs/heads/unstable by this push:
     new 46e7eaf4 Fix ZRANGE command should return an empty array when count = 0 (#1492)
46e7eaf4 is described below

commit 46e7eaf428c3e4935cc73f70be3f8c9066d5cb3c
Author: clundro <in...@outlook.com>
AuthorDate: Thu Jun 15 11:13:31 2023 +0800

    Fix ZRANGE command should return an empty array when count = 0 (#1492)
    
    Currently, ZRANGE will return all matched members if the count = 0 which is not consistent
    with the Redis behavior. For example:
    
    ```shell
    127.0.0.1:6666> zadd zset11 1 a 2 b 3 c 4 d 5 e 6 f 7 g
    (integer) 7
    127.0.0.1:6666> zrange zset11 0 6 byscore limit 0 0
    ```
    
    Redis will return an empty array:
    ```shell
    127.0.0.1:6379> zadd zset11 1 a 2 b 3 c 4 d 5 e 6 f 7 g
    (integer) 7
    127.0.0.1:6379> zrange zset11 0 6 byscore limit 0 0
    (empty array)
    ```
    
    But we got all matched members in ZSET:
    ```shell
    127.0.0.1:6666> zrange zset11 0 6 byscore limit 0 0
    1) "a"
    2) "b"
    3) "c"
    4) "d"
    5) "e"
    6) "f"
    ```
---
 src/commands/cmd_zset.cc                 | 16 +++++++++++++++-
 tests/gocase/unit/type/zset/zset_test.go | 29 +++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/src/commands/cmd_zset.cc b/src/commands/cmd_zset.cc
index 216288fd..6ba6f1d9 100644
--- a/src/commands/cmd_zset.cc
+++ b/src/commands/cmd_zset.cc
@@ -24,6 +24,7 @@
 #include "commander.h"
 #include "commands/scan_base.h"
 #include "error_constants.h"
+#include "server/redis_reply.h"
 #include "server/server.h"
 #include "types/redis_zset.h"
 
@@ -454,6 +455,12 @@ class CommandZRangeStore : public Commander {
   RangeScoreSpec score_spec_;
 };
 
+/*
+ * description:
+ *    syntax:   `ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count]
+ *              [WITHSCORES]`
+ *
+ */
 class CommandZRangeGeneric : public Commander {
  public:
   explicit CommandZRangeGeneric(ZRangeType range_type = kZRangeAuto, ZRangeDirection direction = kZRangeDirectionAuto)
@@ -545,7 +552,6 @@ class CommandZRangeGeneric : public Commander {
 
   Status Execute(Server *svr, Connection *conn, std::string *output) override {
     redis::ZSet zset_db(svr->storage, conn->GetNamespace());
-
     std::vector<MemberScore> member_scores;
 
     rocksdb::Status s;
@@ -555,9 +561,17 @@ class CommandZRangeGeneric : public Commander {
         s = zset_db.RangeByRank(key_, rank_spec_, &member_scores, nullptr);
         break;
       case kZRangeScore:
+        if (score_spec_.count == 0) {
+          *output = redis::MultiBulkString({});
+          return Status::OK();
+        }
         s = zset_db.RangeByScore(key_, score_spec_, &member_scores, nullptr);
         break;
       case kZRangeLex:
+        if (lex_spec_.count == 0) {
+          *output = redis::MultiBulkString({});
+          return Status::OK();
+        }
         s = zset_db.RangeByLex(key_, lex_spec_, &member_scores, nullptr);
         break;
     }
diff --git a/tests/gocase/unit/type/zset/zset_test.go b/tests/gocase/unit/type/zset/zset_test.go
index 872e075a..94a16b61 100644
--- a/tests/gocase/unit/type/zset/zset_test.go
+++ b/tests/gocase/unit/type/zset/zset_test.go
@@ -28,6 +28,7 @@ import (
 	"strconv"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/apache/incubator-kvrocks/tests/gocase/util"
 	"github.com/redis/go-redis/v9"
@@ -439,6 +440,33 @@ func basicTests(t *testing.T, rdb *redis.Client, ctx context.Context, encoding s
 			{4, "d"},
 		}, rdb.ZRangeWithScores(ctx, "ztmp", 0, -1).Val())
 
+		for i := 1; i < 10; i++ {
+			cmd := rdb.ZRangeArgs(ctx, redis.ZRangeArgs{Key: "ztmp", Count: 0, ByScore: true, Start: 0, Stop: -1, Offset: int64(i)})
+			require.NoError(t, cmd.Err())
+			require.Equal(t, []string{}, cmd.Val())
+		}
+
+		// go-redis removes the limit condition when (offset, count) == (0, 0)
+		// so we use (offset, count) = (0, -1)
+		cmd1 := rdb.Do(ctx, "zrange", "ztmp", 0, -1, "byscore", "limit", 0, 0)
+		require.NoError(t, cmd1.Err())
+		require.Equal(t, []interface{}{}, cmd1.Val())
+
+		// limit with zero count
+		for i := 0; i < 20; i++ {
+			var args [3]int64
+			for j := 0; j < 3; j++ {
+				rand.Seed(time.Now().UnixNano())
+				args[j] = rand.Int63n(20) - 10
+			}
+			if args[2] == 0 {
+				continue
+			}
+			cmd := rdb.ZRangeArgs(ctx, redis.ZRangeArgs{Key: "ztmp", Count: 0, ByScore: true, Start: args[0], Stop: args[1], Offset: args[2]})
+			require.NoError(t, cmd.Err())
+			require.Equal(t, []string{}, cmd.Val())
+		}
+
 		// extend zrange commands
 		require.Equal(t, []string{"a", "b", "c", "d"}, rdb.ZRangeArgs(ctx, redis.ZRangeArgs{Key: "ztmp", Start: 0, Stop: -1, Offset: 0, Count: -1}).Val())
 		require.Equal(t, []string{"d", "c", "b", "a"}, rdb.ZRangeArgs(ctx, redis.ZRangeArgs{Key: "ztmp", Start: 0, Stop: -1, Offset: 0, Count: -1, Rev: true}).Val())
@@ -454,6 +482,7 @@ func basicTests(t *testing.T, rdb *redis.Client, ctx context.Context, encoding s
 			{2, "b"},
 			{1, "a"},
 		}, rdb.ZRangeArgsWithScores(ctx, redis.ZRangeArgs{Key: "ztmp", Start: 0, Stop: -1, Offset: 0, Count: -1, Rev: true}).Val())
+
 	})
 
 	t.Run(fmt.Sprintf("ZREVRANGE basics - %s", encoding), func(t *testing.T) {