Better RENAME/RENAMENX tests

This commit is contained in:
tidwall 2022-09-23 11:18:01 -07:00
parent ede1ce0269
commit 960c860b3a
4 changed files with 103 additions and 74 deletions

View File

@ -244,10 +244,11 @@ func (s *Server) cmdGET(msg *Message) (resp.Value, error) {
if i > 0 { if i > 0 {
buf.WriteString(`,`) buf.WriteString(`,`)
} }
buf.WriteString(jsonString(f.Name()) + ":" + f.Value().JSON()) buf.WriteString(jsonString(f.Name()) + ":" +
f.Value().JSON())
} else { } else {
fvals = append(fvals, fvals = append(fvals, resp.StringValue(f.Name()),
resp.StringValue(f.Name()), resp.StringValue(f.Value().Data())) resp.StringValue(f.Value().Data()))
} }
i++ i++
return true return true
@ -441,7 +442,8 @@ func (s *Server) cmdDROP(msg *Message) (resp.Value, commandDetails, error) {
d.timestamp = time.Now() d.timestamp = time.Now()
switch msg.OutputType { switch msg.OutputType {
case JSON: case JSON:
res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") res = resp.StringValue(`{"ok":true,"elapsed":"` +
time.Since(start).String() + "\"}")
case RESP: case RESP:
if d.updated { if d.updated {
res = resp.IntegerValue(1) res = resp.IntegerValue(1)
@ -452,54 +454,67 @@ func (s *Server) cmdDROP(msg *Message) (resp.Value, commandDetails, error) {
return res, d, nil return res, d, nil
} }
func (s *Server) cmdRename(msg *Message) (res resp.Value, d commandDetails, err error) { // RENAME key newkey
nx := msg.Command() == "renamenx" // RENAMENX key newkey
func (s *Server) cmdRENAME(msg *Message) (resp.Value, commandDetails, error) {
start := time.Now() start := time.Now()
vs := msg.Args[1:]
var ok bool // >> Args
if vs, d.key, ok = tokenval(vs); !ok || d.key == "" {
err = errInvalidNumberOfArguments args := msg.Args
return if len(args) != 3 {
return retwerr(errInvalidNumberOfArguments)
} }
if vs, d.newKey, ok = tokenval(vs); !ok || d.newKey == "" { nx := strings.ToLower(args[0]) == "renamenx"
err = errInvalidNumberOfArguments key := args[1]
return newKey := args[2]
}
if len(vs) != 0 { // >> Operation
err = errInvalidNumberOfArguments
return col, _ := s.cols.Get(key)
}
col, _ := s.cols.Get(d.key)
if col == nil { if col == nil {
err = errKeyNotFound return retwerr(errKeyNotFound)
return
} }
var ierr error
s.hooks.Ascend(nil, func(v interface{}) bool { s.hooks.Ascend(nil, func(v interface{}) bool {
h := v.(*Hook) h := v.(*Hook)
if h.Key == d.key || h.Key == d.newKey { if h.Key == key || h.Key == newKey {
err = errKeyHasHooksSet ierr = errKeyHasHooksSet
return false return false
} }
return true return true
}) })
d.command = "rename" if ierr != nil {
newCol, _ := s.cols.Get(d.newKey) return retwerr(ierr)
}
var updated bool
newCol, _ := s.cols.Get(newKey)
if newCol == nil { if newCol == nil {
d.updated = true updated = true
} else if nx { } else if !nx {
d.updated = false s.cols.Delete(newKey)
} else { updated = true
s.cols.Delete(d.newKey)
d.updated = true
} }
if d.updated { if updated {
s.cols.Delete(d.key) s.cols.Delete(key)
s.cols.Set(d.newKey, col) 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() d.timestamp = time.Now()
switch msg.OutputType { switch msg.OutputType {
case JSON: case JSON:
res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") res = resp.StringValue(`{"ok":true,"elapsed":"` +
time.Since(start).String() + "\"}")
case RESP: case RESP:
if !nx { if !nx {
res = resp.SimpleStringValue("OK") res = resp.SimpleStringValue("OK")
@ -509,7 +524,7 @@ func (s *Server) cmdRename(msg *Message) (res resp.Value, d commandDetails, err
res = resp.IntegerValue(0) res = resp.IntegerValue(0)
} }
} }
return return res, d, nil
} }
func (s *Server) cmdFLUSHDB(msg *Message) (res resp.Value, d commandDetails, err error) { 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() d.timestamp = time.Now()
switch msg.OutputType { switch msg.OutputType {
case JSON: case JSON:
res = resp.StringValue(`{"ok":true,"elapsed":"` + time.Since(start).String() + "\"}") res = resp.StringValue(`{"ok":true,"elapsed":"` +
time.Since(start).String() + "\"}")
case RESP: case RESP:
res = resp.SimpleStringValue("OK") 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] // 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) { func (s *Server) cmdSET(msg *Message) (resp.Value, commandDetails, error) {
start := time.Now() start := time.Now()
if s.config.maxMemory() > 0 && s.outOfMemory.on() { if s.config.maxMemory() > 0 && s.outOfMemory.on() {

View File

@ -604,9 +604,9 @@ func (s *Server) commandInScript(msg *Message) (
case "expire": case "expire":
res, d, err = s.cmdEXPIRE(msg) res, d, err = s.cmdEXPIRE(msg)
case "rename": case "rename":
res, d, err = s.cmdRename(msg) res, d, err = s.cmdRENAME(msg)
case "renamenx": case "renamenx":
res, d, err = s.cmdRename(msg) res, d, err = s.cmdRENAME(msg)
case "persist": case "persist":
res, d, err = s.cmdPERSIST(msg) res, d, err = s.cmdPERSIST(msg)
case "ttl": case "ttl":

View File

@ -1028,9 +1028,9 @@ func (s *Server) command(msg *Message, client *Client) (
case "flushdb": case "flushdb":
res, d, err = s.cmdFLUSHDB(msg) res, d, err = s.cmdFLUSHDB(msg)
case "rename": case "rename":
res, d, err = s.cmdRename(msg) res, d, err = s.cmdRENAME(msg)
case "renamenx": case "renamenx":
res, d, err = s.cmdRename(msg) res, d, err = s.cmdRENAME(msg)
case "sethook": case "sethook":
res, d, err = s.cmdSetHook(msg) res, d, err = s.cmdSetHook(msg)
case "delhook": case "delhook":

View File

@ -85,44 +85,56 @@ func keys_DROP_test(mc *mockServer) error {
Do("SCAN", "mykey", "COUNT").Str("0"), Do("SCAN", "mykey", "COUNT").Str("0"),
Do("DROP", "mykey").Str("0"), Do("DROP", "mykey").Str("0"),
Do("SCAN", "mykey", "COUNT").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(),
Do("DROP", "mykey").JSON().OK(), Do("DROP", "mykey").JSON().OK(),
) )
} }
func keys_RENAME_test(mc *mockServer) error { func keys_RENAME_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{ return mc.DoBatch(
{"SET", "mykey", "myid1", "HASH", "9my5xp7"}, {"OK"}, Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(),
{"SET", "mykey", "myid2", "HASH", "9my5xp8"}, {"OK"}, Do("SET", "mykey", "myid2", "HASH", "9my5xp8").OK(),
{"SCAN", "mykey", "COUNT"}, {2}, Do("SCAN", "mykey", "COUNT").Str("2"),
{"RENAME", "mykey", "mynewkey"}, {"OK"}, Do("RENAME", "foo", "mynewkey", "arg3").Err("wrong number of arguments for 'rename' command"),
{"SCAN", "mykey", "COUNT"}, {0}, Do("RENAME", "mykey", "mynewkey").OK(),
{"SCAN", "mynewkey", "COUNT"}, {2}, Do("SCAN", "mykey", "COUNT").Str("0"),
{"SET", "mykey", "myid3", "HASH", "9my5xp7"}, {"OK"}, Do("SCAN", "mynewkey", "COUNT").Str("2"),
{"RENAME", "mykey", "mynewkey"}, {"OK"}, Do("SET", "mykey", "myid3", "HASH", "9my5xp7").OK(),
{"SCAN", "mykey", "COUNT"}, {0}, Do("RENAME", "mykey", "mynewkey").OK(),
{"SCAN", "mynewkey", "COUNT"}, {1}, Do("SCAN", "mykey", "COUNT").Str("0"),
{"RENAME", "foo", "mynewkey"}, {"ERR key not found"}, Do("SCAN", "mynewkey", "COUNT").Str("1"),
{"SCAN", "mynewkey", "COUNT"}, {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 { func keys_RENAMENX_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{ return mc.DoBatch(
{"SET", "mykey", "myid1", "HASH", "9my5xp7"}, {"OK"}, Do("SET", "mykey", "myid1", "HASH", "9my5xp7").OK(),
{"SET", "mykey", "myid2", "HASH", "9my5xp8"}, {"OK"}, Do("SET", "mykey", "myid2", "HASH", "9my5xp8").OK(),
{"SCAN", "mykey", "COUNT"}, {2}, Do("SCAN", "mykey", "COUNT").Str("2"),
{"RENAMENX", "mykey", "mynewkey"}, {1}, Do("RENAMENX", "mykey", "mynewkey").Str("1"),
{"SCAN", "mykey", "COUNT"}, {0}, Do("SCAN", "mykey", "COUNT").Str("0"),
{"DROP", "mykey"}, {0}, Do("DROP", "mykey").Str("0"),
{"SCAN", "mykey", "COUNT"}, {0}, Do("SCAN", "mykey", "COUNT").Str("0"),
{"SCAN", "mynewkey", "COUNT"}, {2}, Do("SCAN", "mynewkey", "COUNT").Str("2"),
{"SET", "mykey", "myid3", "HASH", "9my5xp7"}, {"OK"}, Do("SET", "mykey", "myid3", "HASH", "9my5xp7").OK(),
{"RENAMENX", "mykey", "mynewkey"}, {0}, Do("RENAMENX", "mykey", "mynewkey").Str("0"),
{"SCAN", "mykey", "COUNT"}, {1}, Do("SCAN", "mykey", "COUNT").Str("1"),
{"SCAN", "mynewkey", "COUNT"}, {2}, Do("SCAN", "mynewkey", "COUNT").Str("2"),
{"RENAMENX", "foo", "mynewkey"}, {"ERR key not found"}, Do("RENAMENX", "foo", "mynewkey").Str("ERR key not found"),
{"SCAN", "mynewkey", "COUNT"}, {2}, Do("SCAN", "mynewkey", "COUNT").Str("2"),
}) )
} }
func keys_EXPIRE_test(mc *mockServer) error { func keys_EXPIRE_test(mc *mockServer) error {
return mc.DoBatch([][]interface{}{ return mc.DoBatch([][]interface{}{