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/kvrocks.git
The following commit(s) were added to refs/heads/unstable by this push:
new 0f104e59 Add support of the command RENAME&RENAMENX (#2026)
0f104e59 is described below
commit 0f104e59d3fc1c52e784b18c7ce99d28620acbc0
Author: 纪华裕 <[email protected]>
AuthorDate: Tue Jan 23 16:09:08 2024 +0800
Add support of the command RENAME&RENAMENX (#2026)
Co-authored-by: hulk <[email protected]>
---
src/commands/cmd_key.cc | 34 +-
src/storage/iterator.cc | 2 +
src/storage/iterator.h | 1 +
src/storage/redis_db.cc | 66 ++
src/storage/redis_db.h | 1 +
tests/gocase/unit/rename/rename_test.go | 1044 +++++++++++++++++++++++++++++++
6 files changed, 1147 insertions(+), 1 deletion(-)
diff --git a/src/commands/cmd_key.cc b/src/commands/cmd_key.cc
index 35bfeefd..f94f87fe 100644
--- a/src/commands/cmd_key.cc
+++ b/src/commands/cmd_key.cc
@@ -307,6 +307,36 @@ class CommandDel : public Commander {
}
};
+class CommandRename : public Commander {
+ public:
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ redis::Database redis(srv->storage, conn->GetNamespace());
+ bool ret = true;
+
+ auto s = redis.Rename(args_[1], args_[2], false, &ret);
+ if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
+
+ *output = redis::SimpleString("OK");
+ return Status::OK();
+ }
+};
+
+class CommandRenameNX : public Commander {
+ public:
+ Status Execute(Server *srv, Connection *conn, std::string *output) override {
+ redis::Database redis(srv->storage, conn->GetNamespace());
+ bool ret = true;
+ auto s = redis.Rename(args_[1], args_[2], true, &ret);
+ if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
+ if (ret) {
+ *output = redis::Integer(1);
+ } else {
+ *output = redis::Integer(0);
+ }
+ return Status::OK();
+ }
+};
+
REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandTTL>("ttl", 2, "read-only", 1, 1,
1),
MakeCmdAttr<CommandPTTL>("pttl", 2, "read-only", 1, 1,
1),
MakeCmdAttr<CommandType>("type", 2, "read-only", 1, 1,
1),
@@ -321,6 +351,8 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandTTL>("ttl", 2,
"read-only", 1, 1, 1),
MakeCmdAttr<CommandExpireTime>("expiretime", 2,
"read-only", 1, 1, 1),
MakeCmdAttr<CommandPExpireTime>("pexpiretime", 2,
"read-only", 1, 1, 1),
MakeCmdAttr<CommandDel>("del", -2, "write", 1, -1, 1),
- MakeCmdAttr<CommandDel>("unlink", -2, "write", 1, -1,
1), )
+ MakeCmdAttr<CommandDel>("unlink", -2, "write", 1, -1,
1),
+ MakeCmdAttr<CommandRename>("rename", 3, "write", 1, 2,
1),
+ MakeCmdAttr<CommandRenameNX>("renamenx", 3, "write",
1, 2, 1), )
} // namespace redis
diff --git a/src/storage/iterator.cc b/src/storage/iterator.cc
index 58e283b1..3717afb0 100644
--- a/src/storage/iterator.cc
+++ b/src/storage/iterator.cc
@@ -145,6 +145,8 @@ Slice SubKeyIterator::UserKey() const {
return internal_key.GetSubKey();
}
+rocksdb::ColumnFamilyHandle* SubKeyIterator::ColumnFamilyHandle() const {
return Valid() ? this->cf_handle_ : nullptr; }
+
Slice SubKeyIterator::Value() const { return Valid() ? iter_->value() :
Slice(); }
void SubKeyIterator::Seek() {
diff --git a/src/storage/iterator.h b/src/storage/iterator.h
index 40b93bc3..c257df6f 100644
--- a/src/storage/iterator.h
+++ b/src/storage/iterator.h
@@ -37,6 +37,7 @@ class SubKeyIterator {
Slice Key() const;
// return the user key without prefix
Slice UserKey() const;
+ rocksdb::ColumnFamilyHandle *ColumnFamilyHandle() const;
Slice Value() const;
void Reset();
diff --git a/src/storage/redis_db.cc b/src/storage/redis_db.cc
index a9751bbf..63e8ef57 100644
--- a/src/storage/redis_db.cc
+++ b/src/storage/redis_db.cc
@@ -29,8 +29,11 @@
#include "db_util.h"
#include "parse_util.h"
#include "rocksdb/iterator.h"
+#include "rocksdb/status.h"
#include "server/server.h"
+#include "storage/iterator.h"
#include "storage/redis_metadata.h"
+#include "storage/storage.h"
#include "time_util.h"
namespace redis {
@@ -695,4 +698,67 @@ Status WriteBatchLogData::Decode(const rocksdb::Slice
&blob) {
return Status::OK();
}
+
+rocksdb::Status Database::Rename(const std::string &key, const std::string
&new_key, bool nx, bool *ret) {
+ *ret = true;
+ std::string ns_key = AppendNamespacePrefix(key);
+ std::string new_ns_key = AppendNamespacePrefix(new_key);
+
+ std::vector<std::string> lock_keys = {ns_key, new_ns_key};
+ MultiLockGuard guard(storage_->GetLockManager(), lock_keys);
+
+ RedisType type = kRedisNone;
+ auto s = Type(key, &type);
+ if (!s.ok()) return s;
+ if (type == kRedisNone) return rocksdb::Status::InvalidArgument("ERR no such
key");
+
+ if (nx) {
+ int exist = 0;
+ if (s = Exists({new_key}, &exist), !s.ok()) return s;
+ if (exist > 0) {
+ *ret = false;
+ return rocksdb::Status::OK();
+ }
+ }
+
+ if (key == new_key) return rocksdb::Status::OK();
+
+ auto batch = storage_->GetWriteBatchBase();
+ WriteBatchLogData log_data(type);
+ batch->PutLogData(log_data.Encode());
+
+ engine::DBIterator iter(storage_, rocksdb::ReadOptions());
+ iter.Seek(ns_key);
+
+ // copy metadata
+ batch->Delete(metadata_cf_handle_, ns_key);
+ batch->Put(metadata_cf_handle_, new_ns_key, iter.Value());
+
+ auto subkey_iter = iter.GetSubKeyIterator();
+
+ if (subkey_iter != nullptr) {
+ auto zset_score_cf = type == kRedisZSet ?
storage_->GetCFHandle(engine::kZSetScoreColumnFamilyName) : nullptr;
+
+ for (subkey_iter->Seek(); subkey_iter->Valid(); subkey_iter->Next()) {
+ InternalKey from_ikey(subkey_iter->Key(), storage_->IsSlotIdEncoded());
+ std::string to_ikey =
+ InternalKey(new_ns_key, from_ikey.GetSubKey(),
from_ikey.GetVersion(), storage_->IsSlotIdEncoded()).Encode();
+ // copy sub key
+ batch->Put(subkey_iter->ColumnFamilyHandle(), to_ikey,
subkey_iter->Value());
+
+ // The ZSET type stores an extra score and member field inside
`zset_score` column family
+ // while compared to other composed data structures. The purpose is to
allow to seek by score.
+ if (type == kRedisZSet) {
+ std::string score_bytes = subkey_iter->Value().ToString();
+ score_bytes.append(from_ikey.GetSubKey().ToString());
+ // copy score key
+ std::string score_key =
+ InternalKey(new_ns_key, score_bytes, from_ikey.GetVersion(),
storage_->IsSlotIdEncoded()).Encode();
+ batch->Put(zset_score_cf, score_key, Slice());
+ }
+ }
+ }
+
+ return storage_->Write(storage_->DefaultWriteOptions(),
batch->GetWriteBatch());
+}
} // namespace redis
diff --git a/src/storage/redis_db.h b/src/storage/redis_db.h
index 65804441..0627b651 100644
--- a/src/storage/redis_db.h
+++ b/src/storage/redis_db.h
@@ -61,6 +61,7 @@ class Database {
rocksdb::ColumnFamilyHandle *cf_handle = nullptr);
[[nodiscard]] rocksdb::Status ClearKeysOfSlot(const rocksdb::Slice &ns, int
slot);
[[nodiscard]] rocksdb::Status KeyExist(const std::string &key);
+ [[nodiscard]] rocksdb::Status Rename(const std::string &key, const
std::string &new_key, bool nx, bool *ret);
protected:
engine::Storage *storage_;
diff --git a/tests/gocase/unit/rename/rename_test.go
b/tests/gocase/unit/rename/rename_test.go
new file mode 100644
index 00000000..7bbd4a52
--- /dev/null
+++ b/tests/gocase/unit/rename/rename_test.go
@@ -0,0 +1,1044 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package rename
+
+import (
+ "context"
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/apache/kvrocks/tests/gocase/util"
+ "github.com/redis/go-redis/v9"
+ "github.com/stretchr/testify/require"
+)
+
+func TestRename_String(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("Rename string", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "a1", "world", 0).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello",
10*time.Second).Err())
+ require.NoError(t, rdb.Set(ctx, "a1", "world",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "world").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "a1", "world1", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "a2", "world2", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "a3", "world3", 0).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a3").Val())
+ })
+
+ t.Run("RenameNX string", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.NoError(t, rdb.Set(ctx, "a1", "world", 0).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "world", rdb.Get(ctx, "a1").Val())
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
+ })
+
+}
+
+func TestRename_JSON(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ setCmd := "JSON.SET"
+ getCmd := "JSON.GET"
+ jsonA := `{"x":1,"y":2}`
+ jsonB := `{"x":1}`
+
+ t.Run("Rename json", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a1", "$", jsonB).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a1", "$", jsonA).Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "world").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a1", "$", jsonB).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a2", "$", jsonB).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a3", "$", jsonB).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a1").Val())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a2").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a3").Val())
+ })
+
+ t.Run("RenameNX json", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a1", "$", jsonB).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonB, rdb.Do(ctx, getCmd, "a1").Val())
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, setCmd, "a", "$", jsonA).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
+ })
+
+}
+
+func TestRename_List(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualListValues := func(t *testing.T, key string, value []string) {
+ require.EqualValues(t, len(value), rdb.LLen(ctx, key).Val())
+ for i := 0; i < len(value); i++ {
+ require.EqualValues(t, value[i], rdb.LIndex(ctx, key,
int64(i)).Val())
+ }
+ }
+
+ t.Run("Rename string", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, 3, rdb.LLen(ctx, "a1").Val())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Set(ctx, "a1", "world", 0).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "2").Err())
+ require.NoError(t, rdb.LPush(ctx, "a2", "3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a3", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualListValues(t, "a3", []string{"3", "2", "1"})
+ })
+
+ t.Run("RenameNX string", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "3").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualListValues(t, "a", []string{"3", "2", "1"})
+ EqualListValues(t, "a1", []string{"3"})
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualListValues(t, "a1", []string{"3", "2", "1"})
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.LPush(ctx, "a", "1", "2", "3").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualListValues(t, "a", []string{"3", "2", "1"})
+ })
+
+}
+
+func TestRename_hash(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualListValues := func(t *testing.T, key string, value
map[string]string) {
+ require.EqualValues(t, len(value), rdb.HLen(ctx, key).Val())
+ for subKey := range value {
+ require.EqualValues(t, value[subKey], rdb.HGet(ctx,
key, subKey).Val())
+ }
+ }
+
+ t.Run("Rename hash", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.HSet(ctx, "a1", "a", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.HSet(ctx, "a1", "a", "1").Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.HSet(ctx, "a1", "a", "1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a2", "a", "1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a3", "a", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualListValues(t, "a3", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ })
+
+ t.Run("RenameNX hash", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.NoError(t, rdb.HSet(ctx, "a1", "a", "1").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualListValues(t, "a", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ })
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualListValues(t, "a1", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.HSet(ctx, "a", "a", "1", "b", "2", "c",
"3").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualListValues(t, "a", map[string]string{
+ "a": "1",
+ "b": "2",
+ "c": "3",
+ })
+ })
+
+}
+
+func TestRename_set(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualSetValues := func(t *testing.T, key string, value []string) {
+ require.EqualValues(t, len(value), rdb.SCard(ctx, key).Val())
+ for index := range value {
+ require.EqualValues(t, true, rdb.SIsMember(ctx, key,
value[index]).Val())
+ }
+ }
+
+ t.Run("Rename set", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a1", "a", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.SAdd(ctx, "a1", "1").Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a1", "1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a2", "a2", "1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a3", "a3", "1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualSetValues(t, "a3", []string{"1", "2", "3"})
+ })
+
+ t.Run("RenameNX set", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a1", "1").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualSetValues(t, "a", []string{"1", "2", "3"})
+ EqualSetValues(t, "a1", []string{"1"})
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualSetValues(t, "a1", []string{"1", "2", "3"})
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.SAdd(ctx, "a", "1", "2", "3").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualSetValues(t, "a", []string{"1", "2", "3"})
+
+ })
+
+}
+
+func TestRename_zset(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualZSetValues := func(t *testing.T, key string, value map[string]int)
{
+ require.EqualValues(t, len(value), rdb.ZCard(ctx, key).Val())
+ for subKey := range value {
+ score := value[subKey]
+ require.EqualValues(t, []string{subKey},
rdb.ZRangeByScore(ctx, key,
+ &redis.ZRangeBy{Max: strconv.Itoa(score), Min:
strconv.Itoa(score)}).Val())
+ require.EqualValues(t, float64(score), rdb.ZScore(ctx,
key, subKey).Val())
+ }
+ }
+
+ zMember := []redis.Z{{Member: "a", Score: 1}, {Member: "b", Score: 2},
{Member: "c", Score: 3}}
+ zMember2 := []redis.Z{{Member: "a", Score: 2}}
+
+ t.Run("Rename zset", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a1", zMember2...).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a1", zMember2...).Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", 1, 2, 3).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a1", zMember2...).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a2", zMember2...).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a3", zMember2...).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualZSetValues(t, "a3", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+
+ })
+
+ t.Run("RenameNX zset", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a1", zMember2...).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualZSetValues(t, "a", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 2,
+ })
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualZSetValues(t, "a1", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.ZAdd(ctx, "a", zMember...).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualZSetValues(t, "a", map[string]int{
+ "a": 1,
+ "b": 2,
+ "c": 3,
+ })
+
+ })
+
+}
+
+func TestRename_Bitmap(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualBitSetValues := func(t *testing.T, key string, value []int64) {
+ for i := 0; i < len(value); i++ {
+ require.EqualValues(t, int64(value[i]), rdb.Do(ctx,
"BITPOS", key, 1, value[i]/8).Val())
+ }
+ }
+
+ SetBits := func(t *testing.T, key string, value []int64) {
+ for i := 0; i < len(value); i++ {
+ require.NoError(t, rdb.Do(ctx, "SETBIT", key, value[i],
1).Err())
+ }
+ }
+ bitSetA := []int64{16, 1024 * 8 * 2, 1024 * 8 * 12}
+ bitSetB := []int64{1}
+
+ t.Run("Rename Bitmap", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualBitSetValues(t, "a1", bitSetA)
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // newkey has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ SetBits(t, "a1", bitSetB)
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualBitSetValues(t, "a1", bitSetA)
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // newkey has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualBitSetValues(t, "a1", bitSetA)
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ SetBits(t, "a", bitSetA)
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualBitSetValues(t, "a", bitSetA)
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ SetBits(t, "a", bitSetA)
+ SetBits(t, "a1", bitSetB)
+ SetBits(t, "a2", bitSetB)
+ SetBits(t, "a3", bitSetB)
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualBitSetValues(t, "a3", bitSetA)
+ })
+
+ t.Run("RenameNX Bitmap", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ SetBits(t, "a1", bitSetB)
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualBitSetValues(t, "a", bitSetA)
+ EqualBitSetValues(t, "a1", bitSetB)
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualBitSetValues(t, "a1", bitSetA)
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ SetBits(t, "a", bitSetA)
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualBitSetValues(t, "a", bitSetA)
+
+ })
+
+}
+
+func TestRename_SInt(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ EqualSIntValues := func(t *testing.T, key string, value []int) {
+ require.EqualValues(t, len(value), rdb.Do(ctx, "SICARD",
key).Val())
+ for i := 0; i < len(value); i++ {
+ require.EqualValues(t, []interface{}{int64(1)},
rdb.Do(ctx, "SIEXISTS", key, value[i]).Val())
+ }
+ }
+
+ t.Run("Rename SInt", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a1", 99).Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a1", 85).Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a2", 77, 0).Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a3", 111, 222,
333).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ EqualSIntValues(t, "a3", []int{3, 4, 5, 123, 245})
+ })
+
+ t.Run("RenameNX SInt", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a1", 99).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
+ EqualSIntValues(t, "a1", []int{99})
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, "SIADD", "a", 3, 4, 5, 123,
245).Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
+
+ })
+
+}
+
+func TestRename_Bloom(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ bfAdd := "BF.ADD"
+ bfExists := "BF.EXISTS"
+
+ t.Run("Rename Bloom", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1",
"hello").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a1", "world").Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1",
"hello").Val())
+ require.EqualValues(t, 0, rdb.Do(ctx, bfExists, "a1",
"world").Val())
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1",
"hello").Val())
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a",
"hello").Val())
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a1", "world1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a2", "world2").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a3", "world3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a3",
"hello").Val())
+ })
+
+ t.Run("RenameNX Bloom", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a1", "world").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a",
"hello").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1",
"world").Val())
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1",
"hello").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, bfAdd, "a", "hello").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a",
"hello").Val())
+ })
+}
+
+func TestRename_Stream(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ XADD := "XADD"
+ XREAD := "XREAD"
+ t.Run("Rename Stream", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1",
"0").String(), "hello")
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // to-key has value with TTL
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.Expire(ctx, "a", 10*time.Second).Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a1", "*", "a",
"world").Err())
+ require.NoError(t, rdb.Expire(ctx, "a1",
1000*time.Second).Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1",
"0").String(), "hello")
+ util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second,
10*time.Second)
+
+ // to-key has value that not same type
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.LPush(ctx, "a1", "a").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1",
"0").String(), "hello")
+ require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a").Err())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a",
"0").String(), "hello")
+
+ // rename*3
+ require.NoError(t, rdb.Del(ctx, "a", "a1", "a2", "a3").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a1", "*", "a",
"world1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a2", "*", "a",
"world2").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a3", "*", "a",
"world3").Err())
+ require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Rename(ctx, "a1", "a2").Err())
+ require.NoError(t, rdb.Rename(ctx, "a2", "a3").Err())
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a1").Val())
+ require.EqualValues(t, "", rdb.Get(ctx, "a2").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a3",
"0").String(), "hello")
+ })
+
+ t.Run("RenameNX Stream", func(t *testing.T) {
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a1", "*", "a",
"world").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a",
"a1").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a",
"0").String(), "hello")
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1",
"0").String(), "world")
+
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1",
"0").String(), "hello")
+ require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+
+ // key == newkey
+ require.NoError(t, rdb.Del(ctx, "a", "a1").Err())
+ require.NoError(t, rdb.Do(ctx, XADD, "a", "*", "a",
"hello").Err())
+ require.EqualValues(t, false, rdb.RenameNX(ctx, "a", "a").Val())
+ require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a",
"0").String(), "hello")
+ })
+}
+
+func TestRename_Error(t *testing.T) {
+ srv := util.StartServer(t, map[string]string{})
+ defer srv.Close()
+
+ ctx := context.Background()
+ rdb := srv.NewClient()
+ defer func() { require.NoError(t, rdb.Close()) }()
+
+ t.Run("Rename from empty key", func(t *testing.T) {
+ require.Error(t, rdb.Rename(ctx, ".empty", "a").Err())
+ require.Error(t, rdb.RenameNX(ctx, ".empty", "a").Err())
+ })
+
+}