Skip to content

Commit

Permalink
Fix ZRANGESTORE not overwrite the dst key (#1609)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
enjoy-binbin authored Jul 25, 2023
1 parent acf2e9c commit 0e28dac
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/commands/cmd_zset.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()};
}
Expand Down
27 changes: 26 additions & 1 deletion tests/gocase/unit/type/zset/zset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 0e28dac

Please sign in to comment.