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 ed5937ce Add support of the command COPY (#2238)
ed5937ce is described below

commit ed5937ce391c3949439d58ceba89908bf0c2c34d
Author: Chiro11 <[email protected]>
AuthorDate: Fri Apr 12 21:03:11 2024 +0800

    Add support of the command COPY (#2238)
---
 src/commands/cmd_key.cc                            |  84 +++-
 src/storage/redis_db.cc                            |  16 +-
 src/storage/redis_db.h                             |   8 +-
 .../{rename/rename_test.go => copy/copy_test.go}   | 531 ++++++++++++---------
 tests/gocase/unit/rename/rename_test.go            |  22 +-
 5 files changed, 391 insertions(+), 270 deletions(-)

diff --git a/src/commands/cmd_key.cc b/src/commands/cmd_key.cc
index 8576db4f..589fa1ed 100644
--- a/src/commands/cmd_key.cc
+++ b/src/commands/cmd_key.cc
@@ -83,14 +83,13 @@ class CommandMoveX : public Commander {
         break;
     }
 
-    bool ret = true;
-    bool key_exist = true;
+    Database::CopyResult res = Database::CopyResult::DONE;
     std::string ns_key = redis.AppendNamespacePrefix(key);
     std::string new_ns_key = ComposeNamespaceKey(ns, key, 
srv->storage->IsSlotIdEncoded());
-    auto s = redis.Move(ns_key, new_ns_key, true, &ret, &key_exist);
+    auto s = redis.Copy(ns_key, new_ns_key, true, true, &res);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
 
-    if (ret && key_exist) {
+    if (res == Database::CopyResult::DONE) {
       *output = redis::Integer(1);
     } else {
       *output = redis::Integer(0);
@@ -222,7 +221,7 @@ class CommandExpireAt : public Commander {
 
     timestamp_ = *parse_result;
 
-    return Commander::Parse(args);
+    return Status::OK();
   }
 
   Status Execute(Server *srv, Connection *conn, std::string *output) override {
@@ -346,14 +345,12 @@ 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;
-    bool key_exist = true;
+    Database::CopyResult res = Database::CopyResult::DONE;
     std::string ns_key = redis.AppendNamespacePrefix(args_[1]);
     std::string new_ns_key = redis.AppendNamespacePrefix(args_[2]);
-    auto s = redis.Move(ns_key, new_ns_key, false, &ret, &key_exist);
+    auto s = redis.Copy(ns_key, new_ns_key, false, true, &res);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
-    if (!key_exist) return {Status::RedisExecErr, 
rocksdb::Status::InvalidArgument("ERR no such key").ToString()};
-
+    if (res == Database::CopyResult::KEY_NOT_EXIST) return 
{Status::RedisExecErr, "no such key"};
     *output = redis::SimpleString("OK");
     return Status::OK();
   }
@@ -363,20 +360,68 @@ 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;
-    bool key_exist = true;
+    Database::CopyResult res = Database::CopyResult::DONE;
     std::string ns_key = redis.AppendNamespacePrefix(args_[1]);
     std::string new_ns_key = redis.AppendNamespacePrefix(args_[2]);
-    auto s = redis.Move(ns_key, new_ns_key, true, &ret, &key_exist);
+    auto s = redis.Copy(ns_key, new_ns_key, true, true, &res);
     if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
-    if (!key_exist) return {Status::RedisExecErr, 
rocksdb::Status::InvalidArgument("ERR no such key").ToString()};
-    if (ret) {
-      *output = redis::Integer(1);
-    } else {
-      *output = redis::Integer(0);
+    switch (res) {
+      case Database::CopyResult::KEY_NOT_EXIST:
+        return {Status::RedisExecErr, "no such key"};
+      case Database::CopyResult::DONE:
+        *output = redis::Integer(1);
+        break;
+      case Database::CopyResult::KEY_ALREADY_EXIST:
+        *output = redis::Integer(0);
+        break;
+    }
+    return Status::OK();
+  }
+};
+
+class CommandCopy : public Commander {
+ public:
+  Status Parse(const std::vector<std::string> &args) override {
+    CommandParser parser(args, 3);
+    while (parser.Good()) {
+      if (parser.EatEqICase("db")) {
+        auto db_num = GET_OR_RET(parser.TakeInt());
+        // There's only one database in Kvrocks, so the DB must be 0 here.
+        if (db_num != 0) {
+          return {Status::RedisParseErr, errInvalidSyntax};
+        }
+      } else if (parser.EatEqICase("replace")) {
+        replace_ = true;
+      } else {
+        return parser.InvalidSyntax();
+      }
     }
+
     return Status::OK();
   }
+
+  Status Execute(Server *srv, Connection *conn, std::string *output) override {
+    redis::Database redis(srv->storage, conn->GetNamespace());
+    Database::CopyResult res = Database::CopyResult::DONE;
+    std::string ns_key = redis.AppendNamespacePrefix(args_[1]);
+    std::string new_ns_key = redis.AppendNamespacePrefix(args_[2]);
+    auto s = redis.Copy(ns_key, new_ns_key, !replace_, false, &res);
+    if (!s.ok()) return {Status::RedisExecErr, s.ToString()};
+    switch (res) {
+      case Database::CopyResult::KEY_NOT_EXIST:
+        return {Status::RedisExecErr, "no such key"};
+      case Database::CopyResult::DONE:
+        *output = redis::Integer(1);
+        break;
+      case Database::CopyResult::KEY_ALREADY_EXIST:
+        *output = redis::Integer(0);
+        break;
+    }
+    return Status::OK();
+  }
+
+ private:
+  bool replace_ = false;
 };
 
 REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandTTL>("ttl", 2, "read-only", 1, 1, 
1),
@@ -396,6 +441,7 @@ REDIS_REGISTER_COMMANDS(MakeCmdAttr<CommandTTL>("ttl", 2, 
"read-only", 1, 1, 1),
                         MakeCmdAttr<CommandDel>("del", -2, "write 
no-dbsize-check", 1, -1, 1),
                         MakeCmdAttr<CommandDel>("unlink", -2, "write 
no-dbsize-check", 1, -1, 1),
                         MakeCmdAttr<CommandRename>("rename", 3, "write", 1, 2, 
1),
-                        MakeCmdAttr<CommandRenameNX>("renamenx", 3, "write", 
1, 2, 1), )
+                        MakeCmdAttr<CommandRenameNX>("renamenx", 3, "write", 
1, 2, 1),
+                        MakeCmdAttr<CommandCopy>("copy", -3, "write", 1, 2, 
1), )
 
 }  // namespace redis
diff --git a/src/storage/redis_db.cc b/src/storage/redis_db.cc
index 49b6ab2a..fab6562b 100644
--- a/src/storage/redis_db.cc
+++ b/src/storage/redis_db.cc
@@ -708,10 +708,8 @@ rocksdb::Status Database::typeInternal(const Slice &key, 
RedisType *type) {
   return rocksdb::Status::OK();
 }
 
-rocksdb::Status Database::Move(const std::string &key, const std::string 
&new_key, bool nx, bool *ret,
-                               bool *key_exist) {
-  *ret = true;
-  *key_exist = true;
+rocksdb::Status Database::Copy(const std::string &key, const std::string 
&new_key, bool nx, bool delete_old,
+                               CopyResult *res) {
   std::vector<std::string> lock_keys = {key, new_key};
   MultiLockGuard guard(storage_->GetLockManager(), lock_keys);
 
@@ -719,7 +717,7 @@ rocksdb::Status Database::Move(const std::string &key, 
const std::string &new_ke
   auto s = typeInternal(key, &type);
   if (!s.ok()) return s;
   if (type == kRedisNone) {
-    *key_exist = false;
+    *res = CopyResult::KEY_NOT_EXIST;
     return rocksdb::Status::OK();
   }
 
@@ -727,11 +725,13 @@ rocksdb::Status Database::Move(const std::string &key, 
const std::string &new_ke
     int exist = 0;
     if (s = existsInternal({new_key}, &exist), !s.ok()) return s;
     if (exist > 0) {
-      *ret = false;
+      *res = CopyResult::KEY_ALREADY_EXIST;
       return rocksdb::Status::OK();
     }
   }
 
+  *res = CopyResult::DONE;
+
   if (key == new_key) return rocksdb::Status::OK();
 
   auto batch = storage_->GetWriteBatchBase();
@@ -741,8 +741,10 @@ rocksdb::Status Database::Move(const std::string &key, 
const std::string &new_ke
   engine::DBIterator iter(storage_, rocksdb::ReadOptions());
   iter.Seek(key);
 
+  if (delete_old) {
+    batch->Delete(metadata_cf_handle_, key);
+  }
   // copy metadata
-  batch->Delete(metadata_cf_handle_, key);
   batch->Put(metadata_cf_handle_, new_key, iter.Value());
 
   auto subkey_iter = iter.GetSubKeyIterator();
diff --git a/src/storage/redis_db.h b/src/storage/redis_db.h
index 909e967b..31de41dc 100644
--- a/src/storage/redis_db.h
+++ b/src/storage/redis_db.h
@@ -101,9 +101,11 @@ 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);
-  // Move <key,value> to <new_key,value> (already an internal key)
-  [[nodiscard]] rocksdb::Status Move(const std::string &key, const std::string 
&new_key, bool nx, bool *ret,
-                                     bool *key_exist);
+
+  // Copy <key,value> to <new_key,value> (already an internal key)
+  enum class CopyResult { KEY_NOT_EXIST, KEY_ALREADY_EXIST, DONE };
+  [[nodiscard]] rocksdb::Status Copy(const std::string &key, const std::string 
&new_key, bool nx, bool delete_old,
+                                     CopyResult *res);
 
  protected:
   engine::Storage *storage_;
diff --git a/tests/gocase/unit/rename/rename_test.go 
b/tests/gocase/unit/copy/copy_test.go
similarity index 68%
copy from tests/gocase/unit/rename/rename_test.go
copy to tests/gocase/unit/copy/copy_test.go
index 7bbd4a52..869c9d93 100644
--- a/tests/gocase/unit/rename/rename_test.go
+++ b/tests/gocase/unit/copy/copy_test.go
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-package rename
+package copycmd
 
 import (
        "context"
@@ -30,7 +30,7 @@ import (
        "github.com/stretchr/testify/require"
 )
 
-func TestRename_String(t *testing.T) {
+func TestCopyString(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -38,11 +38,11 @@ func TestRename_String(t *testing.T) {
        rdb := srv.NewClient()
        defer func() { require.NoError(t, rdb.Close()) }()
 
-       t.Run("Rename string", func(t *testing.T) {
+       t.Run("Copy string replace", 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
                require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -50,8 +50,8 @@ func TestRename_String(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.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
                require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -59,8 +59,8 @@ func TestRename_String(t *testing.T) {
                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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, "hello", 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)
 
@@ -68,56 +68,56 @@ func TestRename_String(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.LPush(ctx, "a1", "world").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, "hello", 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.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
 
-               // rename*3
+               // copy * 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
+               require.EqualValues(t, "hello", rdb.Get(ctx, "a1").Val())
+               require.EqualValues(t, "hello", rdb.Get(ctx, "a2").Val())
                require.EqualValues(t, "hello", rdb.Get(ctx, "a3").Val())
        })
 
-       t.Run("RenameNX string", func(t *testing.T) {
+       t.Run("Copy string not replace", 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, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               require.EqualValues(t, "hello", 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, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                require.EqualValues(t, "hello", rdb.Get(ctx, "a").Val())
        })
 
 }
 
-func TestRename_JSON(t *testing.T) {
+func TestCopyJSON(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -130,12 +130,12 @@ func TestRename_JSON(t *testing.T) {
        jsonA := `{"x":1,"y":2}`
        jsonB := `{"x":1}`
 
-       t.Run("Rename json", func(t *testing.T) {
+       t.Run("Copy json replace", 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, jsonA, 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())
 
@@ -143,8 +143,8 @@ func TestRename_JSON(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.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, nil, rdb.Do(ctx, getCmd, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, jsonA, 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())
 
@@ -154,8 +154,8 @@ func TestRename_JSON(t *testing.T) {
                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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, jsonA, 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)
 
@@ -163,56 +163,56 @@ func TestRename_JSON(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.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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, jsonA, 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.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
 
-               // rename*3
+               // copy * 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
+               require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a1").Val())
+               require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a2").Val())
                require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a3").Val())
        })
 
-       t.Run("RenameNX json", func(t *testing.T) {
+       t.Run("Copy json not replace", 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, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               require.EqualValues(t, jsonA, 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, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                require.EqualValues(t, jsonA, rdb.Do(ctx, getCmd, "a").Val())
        })
 
 }
 
-func TestRename_List(t *testing.T) {
+func TestCopyList(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -227,11 +227,11 @@ func TestRename_List(t *testing.T) {
                }
        }
 
-       t.Run("Rename string", func(t *testing.T) {
+       t.Run("Copy string replace", 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, 3, rdb.LLen(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())
@@ -240,8 +240,8 @@ func TestRename_List(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", "a").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", []string{"3", "2", "1"})
                EqualListValues(t, "a1", []string{"3", "2", "1"})
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -251,8 +251,8 @@ func TestRename_List(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", []string{"3", "2", "1"})
                EqualListValues(t, "a1", []string{"3", "2", "1"})
                util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second, 
10*time.Second)
 
@@ -260,56 +260,56 @@ func TestRename_List(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.Set(ctx, "a1", "world", 0).Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", []string{"3", "2", "1"})
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualListValues(t, "a1", []string{"3", "2", "1"})
 
-               // rename*3
+               // coopy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualListValues(t, "a", []string{"3", "2", "1"})
+               EqualListValues(t, "a1", []string{"3", "2", "1"})
+               EqualListValues(t, "a2", []string{"3", "2", "1"})
                EqualListValues(t, "a3", []string{"3", "2", "1"})
        })
 
-       t.Run("RenameNX string", func(t *testing.T) {
+       t.Run("Copy string not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               EqualListValues(t, "a", []string{"3", "2", "1"})
                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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualListValues(t, "a", []string{"3", "2", "1"})
        })
 
 }
 
-func TestRename_hash(t *testing.T) {
+func TestCopyHash(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -324,11 +324,15 @@ func TestRename_hash(t *testing.T) {
                }
        }
 
-       t.Run("Rename hash", func(t *testing.T) {
+       t.Run("Copy hash replace", 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                EqualListValues(t, "a1", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -340,8 +344,12 @@ func TestRename_hash(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.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                EqualListValues(t, "a1", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -355,8 +363,12 @@ func TestRename_hash(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                EqualListValues(t, "a1", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -368,8 +380,12 @@ func TestRename_hash(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.LPush(ctx, "a1", "a", "1").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                EqualListValues(t, "a1", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -380,25 +396,37 @@ func TestRename_hash(t *testing.T) {
                // 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualListValues(t, "a1", map[string]string{
                        "a": "1",
                        "b": "2",
                        "c": "3",
                })
 
-               // rename*3
+               // copy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
+               EqualListValues(t, "a1", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
+               EqualListValues(t, "a2", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                EqualListValues(t, "a3", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -406,11 +434,11 @@ func TestRename_hash(t *testing.T) {
                })
        })
 
-       t.Run("RenameNX hash", func(t *testing.T) {
+       t.Run("Copy hash not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
                EqualListValues(t, "a", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -422,18 +450,22 @@ func TestRename_hash(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.EqualValues(t, true, rdb.RenameNX(ctx, "a", "a1").Val())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               EqualListValues(t, "a", map[string]string{
+                       "a": "1",
+                       "b": "2",
+                       "c": "3",
+               })
                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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualListValues(t, "a", map[string]string{
                        "a": "1",
                        "b": "2",
@@ -443,7 +475,7 @@ func TestRename_hash(t *testing.T) {
 
 }
 
-func TestRename_set(t *testing.T) {
+func TestCopySet(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -458,11 +490,11 @@ func TestRename_set(t *testing.T) {
                }
        }
 
-       t.Run("Rename set", func(t *testing.T) {
+       t.Run("Copy set replace", 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
                EqualSetValues(t, "a1", []string{"1", "2", "3"})
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -470,8 +502,8 @@ func TestRename_set(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", "a", "1").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
                EqualSetValues(t, "a1", []string{"1", "2", "3"})
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -481,8 +513,8 @@ func TestRename_set(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
                EqualSetValues(t, "a1", []string{"1", "2", "3"})
                util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second, 
10*time.Second)
 
@@ -490,57 +522,57 @@ func TestRename_set(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.LPush(ctx, "a1", "1").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualSetValues(t, "a1", []string{"1", "2", "3"})
 
-               // rename*3
+               // copy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
+               EqualSetValues(t, "a1", []string{"1", "2", "3"})
+               EqualSetValues(t, "a2", []string{"1", "2", "3"})
                EqualSetValues(t, "a3", []string{"1", "2", "3"})
        })
 
-       t.Run("RenameNX set", func(t *testing.T) {
+       t.Run("Copy set not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
                EqualSetValues(t, "a1", []string{"1", "2", "3"})
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               EqualSetValues(t, "a", []string{"1", "2", "3"})
 
                // 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualSetValues(t, "a", []string{"1", "2", "3"})
 
        })
 
 }
 
-func TestRename_zset(t *testing.T) {
+func TestCopyZset(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -561,11 +593,15 @@ func TestRename_zset(t *testing.T) {
        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) {
+       t.Run("Copy 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                EqualZSetValues(t, "a1", map[string]int{
                        "a": 1,
                        "b": 2,
@@ -577,8 +613,12 @@ func TestRename_zset(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.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                EqualZSetValues(t, "a1", map[string]int{
                        "a": 1,
                        "b": 2,
@@ -592,8 +632,12 @@ func TestRename_zset(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                EqualZSetValues(t, "a1", map[string]int{
                        "a": 1,
                        "b": 2,
@@ -605,8 +649,12 @@ func TestRename_zset(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.LPush(ctx, "a1", 1, 2, 3).Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                EqualZSetValues(t, "a1", map[string]int{
                        "a": 1,
                        "b": 2,
@@ -617,37 +665,48 @@ func TestRename_zset(t *testing.T) {
                // 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualZSetValues(t, "a1", map[string]int{
                        "a": 1,
                        "b": 2,
                        "c": 3,
                })
-               // rename*3
+               // copy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
+               EqualZSetValues(t, "a1", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
+               EqualZSetValues(t, "a2", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                EqualZSetValues(t, "a3", map[string]int{
                        "a": 1,
                        "b": 2,
                        "c": 3,
                })
-
        })
 
-       t.Run("RenameNX zset", func(t *testing.T) {
+       t.Run("Copy zset not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
                EqualZSetValues(t, "a", map[string]int{
                        "a": 1,
                        "b": 2,
@@ -659,29 +718,32 @@ func TestRename_zset(t *testing.T) {
 
                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())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               EqualZSetValues(t, "a", map[string]int{
+                       "a": 1,
+                       "b": 2,
+                       "c": 3,
+               })
                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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualZSetValues(t, "a", map[string]int{
                        "a": 1,
                        "b": 2,
                        "c": 3,
                })
-
        })
 
 }
 
-func TestRename_Bitmap(t *testing.T) {
+func TestCopyBitmap(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -703,11 +765,11 @@ func TestRename_Bitmap(t *testing.T) {
        bitSetA := []int64{16, 1024 * 8 * 2, 1024 * 8 * 12}
        bitSetB := []int64{1}
 
-       t.Run("Rename Bitmap", func(t *testing.T) {
+       t.Run("Copy bitmap replace", 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualBitSetValues(t, "a", bitSetA)
                EqualBitSetValues(t, "a1", bitSetA)
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -717,8 +779,8 @@ func TestRename_Bitmap(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualBitSetValues(t, "a", bitSetA)
                EqualBitSetValues(t, "a1", bitSetA)
                util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second, 
10*time.Second)
 
@@ -726,57 +788,56 @@ func TestRename_Bitmap(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualBitSetValues(t, "a", bitSetA)
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualBitSetValues(t, "a", bitSetA)
 
-               // rename*3
+               // copy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualBitSetValues(t, "a", bitSetA)
+               EqualBitSetValues(t, "a1", bitSetA)
+               EqualBitSetValues(t, "a2", bitSetA)
                EqualBitSetValues(t, "a3", bitSetA)
        })
 
-       t.Run("RenameNX Bitmap", func(t *testing.T) {
+       t.Run("Copy bitmap not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               EqualBitSetValues(t, "a", bitSetA)
                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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualBitSetValues(t, "a", bitSetA)
-
        })
 
 }
 
-func TestRename_SInt(t *testing.T) {
+func TestCopySint(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -791,11 +852,11 @@ func TestRename_SInt(t *testing.T) {
                }
        }
 
-       t.Run("Rename SInt", func(t *testing.T) {
+       t.Run("Copy sint replace", 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
                EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -805,8 +866,8 @@ func TestRename_SInt(t *testing.T) {
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
                EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
                util.BetweenValues(t, rdb.TTL(ctx, "a1").Val(), time.Second, 
10*time.Second)
 
@@ -814,57 +875,57 @@ func TestRename_SInt(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.LPush(ctx, "a1", "a").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
                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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
 
-               // rename*3
+               // copy * 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())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
+               EqualSIntValues(t, "a1", []int{3, 4, 5, 123, 245})
+               EqualSIntValues(t, "a2", []int{3, 4, 5, 123, 245})
                EqualSIntValues(t, "a3", []int{3, 4, 5, 123, 245})
        })
 
-       t.Run("RenameNX SInt", func(t *testing.T) {
+       t.Run("Copy sint not replace", 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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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())
+               require.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
                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())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                EqualSIntValues(t, "a", []int{3, 4, 5, 123, 245})
 
        })
 
 }
 
-func TestRename_Bloom(t *testing.T) {
+func TestCopyBloom(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -875,11 +936,11 @@ func TestRename_Bloom(t *testing.T) {
        bfAdd := "BF.ADD"
        bfExists := "BF.EXISTS"
 
-       t.Run("Rename Bloom", func(t *testing.T) {
+       t.Run("Copy bloom replace", 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").Val())
                require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1", 
"hello").Val())
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -889,8 +950,8 @@ func TestRename_Bloom(t *testing.T) {
                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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").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)
@@ -899,55 +960,56 @@ func TestRename_Bloom(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.LPush(ctx, "a1", "a").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").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.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").Val())
 
-               // rename*3
+               // copy * 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").Val())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a1", 
"hello").Val())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a2", 
"hello").Val())
                require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a3", 
"hello").Val())
        })
 
-       t.Run("RenameNX Bloom", func(t *testing.T) {
+       t.Run("Copy bloom not replace", 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, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").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, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                require.EqualValues(t, 1, rdb.Do(ctx, bfExists, "a", 
"hello").Val())
        })
+
 }
 
-func TestRename_Stream(t *testing.T) {
+func TestCopyStream(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -957,11 +1019,11 @@ func TestRename_Stream(t *testing.T) {
 
        XADD := "XADD"
        XREAD := "XREAD"
-       t.Run("Rename Stream", func(t *testing.T) {
+       t.Run("Copy stream replace", 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
                require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1", 
"0").String(), "hello")
                require.EqualValues(t, -1, rdb.TTL(ctx, "a1").Val())
 
@@ -971,8 +1033,8 @@ func TestRename_Stream(t *testing.T) {
                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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
                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)
 
@@ -980,55 +1042,56 @@ func TestRename_Stream(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.LPush(ctx, "a1", "a").Err())
-               require.NoError(t, rdb.Rename(ctx, "a", "a1").Err())
-               require.EqualValues(t, "", rdb.Get(ctx, "a").Val())
+               require.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
                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.NoError(t, rdb.Copy(ctx, "a", "a", 0, true).Err())
                require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
 
-               // rename*3
+               // copy * 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.NoError(t, rdb.Copy(ctx, "a", "a1", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a1", "a2", 0, true).Err())
+               require.NoError(t, rdb.Copy(ctx, "a2", "a3", 0, true).Err())
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a1", 
"0").String(), "hello")
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a2", 
"0").String(), "hello")
                require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a3", 
"0").String(), "hello")
        })
 
-       t.Run("RenameNX Stream", func(t *testing.T) {
+       t.Run("Copy stream not replace", 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.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a1", 0, 
false).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.EqualValues(t, int64(1), rdb.Copy(ctx, "a", "a1", 0, 
false).Val())
+               require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
                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.EqualValues(t, int64(0), rdb.Copy(ctx, "a", "a", 0, 
false).Val())
                require.Contains(t, rdb.Do(ctx, XREAD, "STREAMS", "a", 
"0").String(), "hello")
        })
+
 }
 
-func TestRename_Error(t *testing.T) {
+func TestCopyError(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -1036,9 +1099,17 @@ func TestRename_Error(t *testing.T) {
        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())
+       t.Run("Copy to not db 0", func(t *testing.T) {
+               require.NoError(t, rdb.Del(ctx, "a").Err())
+               require.NoError(t, rdb.Set(ctx, "a", "hello", 0).Err())
+               require.Error(t, rdb.Copy(ctx, "", "a", 1, true).Err())
+               require.Error(t, rdb.Copy(ctx, "", "a", 3, false).Err())
+       })
+
+       t.Run("Copy from empty key", func(t *testing.T) {
+               require.NoError(t, rdb.Del(ctx, ".empty", "a").Err())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, ".empty", "a", 
0, false).Val())
+               require.EqualValues(t, int64(0), rdb.Copy(ctx, ".empty", "a", 
0, true).Val())
        })
 
 }
diff --git a/tests/gocase/unit/rename/rename_test.go 
b/tests/gocase/unit/rename/rename_test.go
index 7bbd4a52..7cc0248c 100644
--- a/tests/gocase/unit/rename/rename_test.go
+++ b/tests/gocase/unit/rename/rename_test.go
@@ -30,7 +30,7 @@ import (
        "github.com/stretchr/testify/require"
 )
 
-func TestRename_String(t *testing.T) {
+func TestRenameString(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -117,7 +117,7 @@ func TestRename_String(t *testing.T) {
 
 }
 
-func TestRename_JSON(t *testing.T) {
+func TestRenameJSON(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -212,7 +212,7 @@ func TestRename_JSON(t *testing.T) {
 
 }
 
-func TestRename_List(t *testing.T) {
+func TestRenameList(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -309,7 +309,7 @@ func TestRename_List(t *testing.T) {
 
 }
 
-func TestRename_hash(t *testing.T) {
+func TestRenameHash(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -443,7 +443,7 @@ func TestRename_hash(t *testing.T) {
 
 }
 
-func TestRename_set(t *testing.T) {
+func TestRenameSet(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -540,7 +540,7 @@ func TestRename_set(t *testing.T) {
 
 }
 
-func TestRename_zset(t *testing.T) {
+func TestRenameZset(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -681,7 +681,7 @@ func TestRename_zset(t *testing.T) {
 
 }
 
-func TestRename_Bitmap(t *testing.T) {
+func TestRenameBitmap(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -776,7 +776,7 @@ func TestRename_Bitmap(t *testing.T) {
 
 }
 
-func TestRename_SInt(t *testing.T) {
+func TestRenameSint(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -864,7 +864,7 @@ func TestRename_SInt(t *testing.T) {
 
 }
 
-func TestRename_Bloom(t *testing.T) {
+func TestRenameBloom(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -947,7 +947,7 @@ func TestRename_Bloom(t *testing.T) {
        })
 }
 
-func TestRename_Stream(t *testing.T) {
+func TestRenameStream(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 
@@ -1028,7 +1028,7 @@ func TestRename_Stream(t *testing.T) {
        })
 }
 
-func TestRename_Error(t *testing.T) {
+func TestRenameError(t *testing.T) {
        srv := util.StartServer(t, map[string]string{})
        defer srv.Close()
 

Reply via email to