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())
+       })
+
+}

Reply via email to