This is an automated email from the ASF dual-hosted git repository. binbin 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 0e28daca Fix ZRANGESTORE not overwrite the dst key (#1609) 0e28daca is described below commit 0e28daca80f22cd7193c903ff9b8c91bef2c8f28 Author: Binbin <binloveplay1...@qq.com> AuthorDate: Wed Jul 26 00:01:39 2023 +0800 Fix ZRANGESTORE not overwrite the dst key (#1609) When the code is doing `store`, it calls the add method, which leads us to perform zadd dst xxx in disguise. And this result the following two issues. The first one is we do a type check against dst: ``` 127.0.0.1:6379> set dst value OK 127.0.0.1:6379> zrangestore dst non 0 -1 (integer) 0 127.0.0.1:6379> get dst (nil) 127.0.0.1:6666> set dst value OK 127.0.0.1:6666> zrangestore dst non 0 -1 (error) ERR Invalid argument: WRONGTYPE Operation against a key holding the wrong kind of value ``` The second one is when dst has members, we are doing zadd: ``` 127.0.0.1:6379> zadd dst 1 a 2 b (integer) 2 127.0.0.1:6379> zadd src 3 c 4 d (integer) 2 127.0.0.1:6379> zrangestore dst src 0 -1 (integer) 2 127.0.0.1:6379> zrange dst 0 -1 1) "c" 2) "d" 127.0.0.1:6666> zadd dst 1 a 2 b (integer) 2 127.0.0.1:6666> zadd src 3 c 4 d (integer) 2 127.0.0.1:6666> zrangestore dst src 0 -1 (integer) 2 127.0.0.1:6666> zrange dst 0 -1 1) "a" 2) "b" 3) "c" 4) "d" ``` ZRANGESTORE was added in #1482 --- src/commands/cmd_zset.cc | 4 ++-- tests/gocase/unit/type/zset/zset_test.go | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/commands/cmd_zset.cc b/src/commands/cmd_zset.cc index 6ee32dfd..e97ead7a 100644 --- a/src/commands/cmd_zset.cc +++ b/src/commands/cmd_zset.cc @@ -770,8 +770,8 @@ class CommandZRangeStore : public Commander { return {Status::RedisExecErr, s.ToString()}; } - uint64_t ret = 0; - s = zset_db.Add(dst_, ZAddFlags(), &member_scores, &ret); + uint64_t ret = member_scores.size(); + s = zset_db.Overwrite(dst_, member_scores); if (!s.ok()) { return {Status::RedisExecErr, s.ToString()}; } diff --git a/tests/gocase/unit/type/zset/zset_test.go b/tests/gocase/unit/type/zset/zset_test.go index 847cb576..1d741364 100644 --- a/tests/gocase/unit/type/zset/zset_test.go +++ b/tests/gocase/unit/type/zset/zset_test.go @@ -477,7 +477,7 @@ func basicTests(t *testing.T, rdb *redis.Client, ctx context.Context, encoding s require.Equal(t, []string{"b", "c", "d"}, rdb.ZRange(ctx, "zdst", 0, -1).Val()) rdb.ZRangeStore(ctx, "zdst", redis.ZRangeArgs{Key: "zsrc", Start: 0, Stop: 2}) - require.Equal(t, []string{"a", "b", "c", "d"}, rdb.ZRange(ctx, "zdst", 0, -1).Val()) + require.Equal(t, []string{"a", "b", "c"}, rdb.ZRange(ctx, "zdst", 0, -1).Val()) rdb.Del(ctx, "zdst") rdb.ZRangeStore(ctx, "zdst", redis.ZRangeArgs{Key: "zsrc", Start: 0, Stop: 0}) @@ -514,6 +514,31 @@ func basicTests(t *testing.T, rdb *redis.Client, ctx context.Context, encoding s require.Equal(t, []string{"c", "d", "f"}, rdb.ZRange(ctx, "zdst", 0, -1).Val()) }) + t.Run(fmt.Sprintf("ZRANGESTORE will overwrite dst - %s", encoding), func(t *testing.T) { + rdb.Del(ctx, "zsrc") + rdb.Del(ctx, "zdst") + + // non-existing src key delete the dst key + rdb.ZAdd(ctx, "zdst", redis.Z{Score: 1, Member: "dst_a"}) + require.Equal(t, int64(0), rdb.Do(ctx, "zrangestore", "zdst", "zsrc", 0, -1).Val()) + require.Equal(t, int64(0), rdb.Exists(ctx, "zdst").Val()) + + // non-existing src key delete the dst key (different type) + require.Equal(t, "OK", rdb.Set(ctx, "zdst", 100, 0).Val()) + require.Equal(t, int64(0), rdb.Do(ctx, "zrangestore", "zdst", "zsrc", 0, -1).Val()) + require.Equal(t, int64(0), rdb.Exists(ctx, "zdst").Val()) + + // overwrite the dst key with the result zset instead of adding + rdb.ZAdd(ctx, "zsrc", redis.Z{Score: 1, Member: "src_a"}) + rdb.ZAdd(ctx, "zsrc", redis.Z{Score: 2, Member: "src_b"}) + rdb.ZAdd(ctx, "zdst", redis.Z{Score: 3, Member: "dst_c"}) + require.Equal(t, int64(2), rdb.Do(ctx, "zrangestore", "zdst", "zsrc", 0, -1).Val()) + require.Equal(t, []redis.Z{ + {1, "src_a"}, + {2, "src_b"}, + }, rdb.ZRangeWithScores(ctx, "zdst", 0, -1).Val()) + }) + t.Run(fmt.Sprintf("ZRANGESTORE error - %s", encoding), func(t *testing.T) { rdb.Del(ctx, "zsrc") rdb.Del(ctx, "zdst")