From 960c860b3a793da9fbe99d598809e5069a4dd673 Mon Sep 17 00:00:00 2001 From: tidwall Date: Fri, 23 Sep 2022 11:18:01 -0700 Subject: [PATCH] Better RENAME/RENAMENX tests --- internal/server/crud.go | 95 ++++++++++++++++++++++---------------- internal/server/scripts.go | 4 +- internal/server/server.go | 4 +- tests/keys_test.go | 74 ++++++++++++++++------------- 4 files changed, 103 insertions(+), 74 deletions(-) diff --git a/internal/server/crud.go b/internal/server/crud.go index 9d07ca73..651b5c06 100644 --- a/internal/server/crud.go +++ b/internal/server/crud.go @@ -244,10 +244,11 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) { if i > 0 { buf.WriteString(`,`) } - buf.WriteString(jsonString(f.Name()) + ":" + f.Value().JSON()) + buf.WriteString(jsonString(f.Name()) + ":" + + f.Value().JSON()) } else { - fvals = append(fvals, - resp.StringValue(f.Name()), resp.StringValue(f.Value().Data())) + fvals = append(fvals, resp.StringValue(f.Name()), + resp.StringValue(f.Value().Data())) } i++ return true @@ -441,7 +442,8 @@ func (s *Server) cmdDROP(msg *Message) (resp.Value, commandDetails, error) { d.timestamp = time.Now() switch msg.OutputType { case JSON: - res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") + res = resp.StringValue(`{"ok":true,"elapsed":"` + + time.Since(start).String() + "\"}") case RESP: if d.updated { res = resp.IntegerValue(1) @@ -452,54 +454,67 @@ func (s *Server) cmdDROP(msg *Message) (resp.Value, commandDetails, error) { return res, d, nil } -func (s *Server) cmdRename(msg *Message) (res resp.Value, d commandDetails, err error) { - nx := msg.Command() == "renamenx" +// RENAME key newkey +// RENAMENX key newkey +func (s *Server) cmdRENAME(msg *Message) (resp.Value, commandDetails, error) { start := time.Now() - vs := msg.Args[1:] - var ok bool - if vs, d.key, ok = tokenval(vs); !ok || d.key == "" { - err = errInvalidNumberOfArguments - return + + // >> Args + + args := msg.Args + if len(args) != 3 { + return retwerr(errInvalidNumberOfArguments) } - if vs, d.newKey, ok = tokenval(vs); !ok || d.newKey == "" { - err = errInvalidNumberOfArguments - return - } - if len(vs) != 0 { - err = errInvalidNumberOfArguments - return - } - col, _ := s.cols.Get(d.key) + nx := strings.ToLower(args[0]) == "renamenx" + key := args[1] + newKey := args[2] + + // >> Operation + + col, _ := s.cols.Get(key) if col == nil { - err = errKeyNotFound - return + return retwerr(errKeyNotFound) } + var ierr error s.hooks.Ascend(nil, func(v interface{}) bool { h := v.(*Hook) - if h.Key == d.key || h.Key == d.newKey { - err = errKeyHasHooksSet + if h.Key == key || h.Key == newKey { + ierr = errKeyHasHooksSet return false } return true }) - d.command = "rename" - newCol, _ := s.cols.Get(d.newKey) + if ierr != nil { + return retwerr(ierr) + } + var updated bool + newCol, _ := s.cols.Get(newKey) if newCol == nil { - d.updated = true - } else if nx { - d.updated = false - } else { - s.cols.Delete(d.newKey) - d.updated = true + updated = true + } else if !nx { + s.cols.Delete(newKey) + updated = true } - if d.updated { - s.cols.Delete(d.key) - s.cols.Set(d.newKey, col) + if updated { + s.cols.Delete(key) + s.cols.Set(newKey, col) } + + // >> Response + + var d commandDetails + var res resp.Value + + d.command = "rename" + d.key = key + d.newKey = newKey + d.updated = updated d.timestamp = time.Now() + switch msg.OutputType { case JSON: - res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") + res = resp.StringValue(`{"ok":true,"elapsed":"` + + time.Since(start).String() + "\"}") case RESP: if !nx { res = resp.SimpleStringValue("OK") @@ -509,7 +524,7 @@ func (s *Server) cmdRename(msg *Message) (res resp.Value, d commandDetails, err res = resp.IntegerValue(0) } } - return + return res, d, nil } func (s *Server) cmdFLUSHDB(msg *Message) (res resp.Value, d commandDetails, err error) { @@ -535,7 +550,8 @@ func (s *Server) cmdFLUSHDB(msg *Message) (res resp.Value, d commandDetails, err d.timestamp = time.Now() switch msg.OutputType { case JSON: - res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") + res = resp.StringValue(`{"ok":true,"elapsed":"` + + time.Since(start).String() + "\"}") case RESP: res = resp.SimpleStringValue("OK") } @@ -543,7 +559,8 @@ func (s *Server) cmdFLUSHDB(msg *Message) (res resp.Value, d commandDetails, err } // SET key id [FIELD name value ...] [EX seconds] [NX|XX] -// (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)|(HASH geohash)|(STRING value) +// (OBJECT geojson)|(POINT lat lon z)|(BOUNDS minlat minlon maxlat maxlon)| +// (HASH geohash)|(STRING value) func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) { start := time.Now() if s.config.maxMemory() > 0 && s.outOfMemory.on() { diff --git a/internal/server/scripts.go b/internal/server/scripts.go index 82fed1aa..84f9a713 100644 --- a/internal/server/scripts.go +++ b/internal/server/scripts.go @@ -604,9 +604,9 @@ func (s *Server) commandInScript(msg *Message) ( case "expire": res, d, err = s.cmdEXPIRE(msg) case "rename": - res, d, err = s.cmdRename(msg) + res, d, err = s.cmdRENAME(msg) case "renamenx": - res, d, err = s.cmdRename(msg) + res, d, err = s.cmdRENAME(msg) case "persist": res, d, err = s.cmdPERSIST(msg) case "ttl": diff --git a/internal/server/server.go b/internal/server/server.go index 9842791a..9af3f5f2 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1028,9 +1028,9 @@ func (s *Server) command(msg *Message, client *Client) ( case "flushdb": res, d, err = s.cmdFLUSHDB(msg) case "rename": - res, d, err = s.cmdRename(msg) + res, d, err = s.cmdRENAME(msg) case "renamenx": - res, d, err = s.cmdRename(msg) + res, d, err = s.cmdRENAME(msg) case "sethook": res, d, err = s.cmdSetHook(msg) case "delhook": diff --git a/tests/keys_test.go b/tests/keys_test.go index ee0d92b5..1d3fefcf 100644 --- a/tests/keys_test.go +++ b/tests/keys_test.go @@ -85,44 +85,56 @@ func keys_DROP_test(mc *mockServer) error { Do("SCAN", "mykey", "COUNT").Str("0"), Do("DROP", "mykey").Str("0"), Do("SCAN", "mykey", "COUNT").Str("0"), - Do("SET", "mykey", "myid1", "HASH", "9my5xp7").Str("OK"), + Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(), Do("DROP", "mykey").JSON().OK(), Do("DROP", "mykey").JSON().OK(), ) } func keys_RENAME_test(mc *mockServer) error { - return mc.DoBatch([][]interface{}{ - {"SET", "mykey", "myid1", "HASH", "9my5xp7"}, {"OK"}, - {"SET", "mykey", "myid2", "HASH", "9my5xp8"}, {"OK"}, - {"SCAN", "mykey", "COUNT"}, {2}, - {"RENAME", "mykey", "mynewkey"}, {"OK"}, - {"SCAN", "mykey", "COUNT"}, {0}, - {"SCAN", "mynewkey", "COUNT"}, {2}, - {"SET", "mykey", "myid3", "HASH", "9my5xp7"}, {"OK"}, - {"RENAME", "mykey", "mynewkey"}, {"OK"}, - {"SCAN", "mykey", "COUNT"}, {0}, - {"SCAN", "mynewkey", "COUNT"}, {1}, - {"RENAME", "foo", "mynewkey"}, {"ERR key not found"}, - {"SCAN", "mynewkey", "COUNT"}, {1}, - }) + return mc.DoBatch( + Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(), + Do("SET", "mykey", "myid2", "HASH", "9my5xp8").OK(), + Do("SCAN", "mykey", "COUNT").Str("2"), + Do("RENAME", "foo", "mynewkey", "arg3").Err("wrong number of arguments for 'rename' command"), + Do("RENAME", "mykey", "mynewkey").OK(), + Do("SCAN", "mykey", "COUNT").Str("0"), + Do("SCAN", "mynewkey", "COUNT").Str("2"), + Do("SET", "mykey", "myid3", "HASH", "9my5xp7").OK(), + Do("RENAME", "mykey", "mynewkey").OK(), + Do("SCAN", "mykey", "COUNT").Str("0"), + Do("SCAN", "mynewkey", "COUNT").Str("1"), + Do("RENAME", "foo", "mynewkey").Err("key not found"), + Do("SCAN", "mynewkey", "COUNT").Str("1"), + Do("SETCHAN", "mychan", "INTERSECTS", "mynewkey", "BOUNDS", 10, 10, 20, 20).Str("1"), + Do("RENAME", "mynewkey", "foo2").Err("key has hooks set"), + Do("RENAMENX", "mynewkey", "foo2").Err("key has hooks set"), + Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(), + Do("RENAME", "mykey", "foo2").OK(), + Do("RENAMENX", "foo2", "foo3").Str("1"), + Do("RENAMENX", "foo2", "foo3").Err("key not found"), + Do("RENAME", "foo2", "foo3").JSON().Err("key not found"), + Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(), + Do("RENAMENX", "mykey", "foo3").Str("0"), + Do("RENAME", "foo3", "foo4").JSON().OK(), + ) } func keys_RENAMENX_test(mc *mockServer) error { - return mc.DoBatch([][]interface{}{ - {"SET", "mykey", "myid1", "HASH", "9my5xp7"}, {"OK"}, - {"SET", "mykey", "myid2", "HASH", "9my5xp8"}, {"OK"}, - {"SCAN", "mykey", "COUNT"}, {2}, - {"RENAMENX", "mykey", "mynewkey"}, {1}, - {"SCAN", "mykey", "COUNT"}, {0}, - {"DROP", "mykey"}, {0}, - {"SCAN", "mykey", "COUNT"}, {0}, - {"SCAN", "mynewkey", "COUNT"}, {2}, - {"SET", "mykey", "myid3", "HASH", "9my5xp7"}, {"OK"}, - {"RENAMENX", "mykey", "mynewkey"}, {0}, - {"SCAN", "mykey", "COUNT"}, {1}, - {"SCAN", "mynewkey", "COUNT"}, {2}, - {"RENAMENX", "foo", "mynewkey"}, {"ERR key not found"}, - {"SCAN", "mynewkey", "COUNT"}, {2}, - }) + return mc.DoBatch( + Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(), + Do("SET", "mykey", "myid2", "HASH", "9my5xp8").OK(), + Do("SCAN", "mykey", "COUNT").Str("2"), + Do("RENAMENX", "mykey", "mynewkey").Str("1"), + Do("SCAN", "mykey", "COUNT").Str("0"), + Do("DROP", "mykey").Str("0"), + Do("SCAN", "mykey", "COUNT").Str("0"), + Do("SCAN", "mynewkey", "COUNT").Str("2"), + Do("SET", "mykey", "myid3", "HASH", "9my5xp7").OK(), + Do("RENAMENX", "mykey", "mynewkey").Str("0"), + Do("SCAN", "mykey", "COUNT").Str("1"), + Do("SCAN", "mynewkey", "COUNT").Str("2"), + Do("RENAMENX", "foo", "mynewkey").Str("ERR key not found"), + Do("SCAN", "mynewkey", "COUNT").Str("2"), + ) } func keys_EXPIRE_test(mc *mockServer) error { return mc.DoBatch([][]interface{}{