diff --git a/go.mod b/go.mod index 32363b994..c60a63432 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.0 require ( github.com/JaderDias/movingmedian v0.0.0-20220813210630-d8c6b6de8835 - github.com/alicebob/miniredis/v2 v2.35.0 + github.com/alicebob/miniredis/v2 v2.36.1 github.com/ansel1/merry v1.8.1 github.com/ansel1/merry/v2 v2.2.2 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df diff --git a/go.sum b/go.sum index d0445f0fa..9cdf3c110 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/Shopify/sarama v1.29.0/go.mod h1:2QpgD79wpdAESqNQMxNc0KYMkycd4slxGdV3 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 h1:xlwdaKcTNVW4PtpQb8aKA4Pjy0CdJHEqvFbAnvR5m2g= github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794/go.mod h1:7e+I0LQFUI9AXWxOfsQROs9xPhoJtbsyWcjJqDd4KPY= -github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= -github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= +github.com/alicebob/miniredis/v2 v2.36.1 h1:Dvc5oAnNOr7BIfPn7tF269U8DvRW1dBG2D5n0WrfYMI= +github.com/alicebob/miniredis/v2 v2.36.1/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/ansel1/merry v1.8.1 h1:z2o6oeJiJ7WNuBp6XAW6BQScBl6vULWxGdw5A/BHJgQ= github.com/ansel1/merry v1.8.1/go.mod h1:wJVu1mHEtEUWq5zTTX9RiWjcE+xL8y7BGYl2VTYdP7M= github.com/ansel1/merry/v2 v2.2.2 h1:/R8URU5LtiYwP7UI1KoZW4ex4nTzr+/T49+TYZsjUas= diff --git a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md index a475c1bb0..b2c840c64 100644 --- a/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md +++ b/vendor/github.com/alicebob/miniredis/v2/CHANGELOG.md @@ -1,6 +1,21 @@ ## Changelog +## v2.36.1 +- support CLUSTER SHARDS (thanks @dadrus) + + +## v2.36.0 + +- return actual server address by CLUSTER NODES (thanks @nastik-kum) +- support DUMP and RESTORE (thanks @alyssaruth) +- support EVALRO (thanks @max-frank) +- add WAIT command as no-op (thanks @aroullet) +- support info stats (thanks @destinyoooo) +- add "-*" keys +- compare against Redis 8.4.0 + + ## v2.35.0 - add Lua redis.setresp({2,3}) diff --git a/vendor/github.com/alicebob/miniredis/v2/README.md b/vendor/github.com/alicebob/miniredis/v2/README.md index 272362e0e..b2282ffb1 100644 --- a/vendor/github.com/alicebob/miniredis/v2/README.md +++ b/vendor/github.com/alicebob/miniredis/v2/README.md @@ -36,6 +36,7 @@ Implemented commands: - Key - COPY - DEL + - DUMP -- partly, only handles string keys - EXISTS - EXPIRE - EXPIREAT @@ -50,11 +51,13 @@ Implemented commands: - RANDOMKEY -- see m.Seed(...) - RENAME - RENAMENX + - RESTORE -- partly, only handles string keys - SCAN - TOUCH - TTL - TYPE - UNLINK + - WAIT -- no-op - Transactions (complete) - DISCARD - EXEC @@ -222,6 +225,7 @@ Implemented commands: - CLUSTER SLOTS - CLUSTER KEYSLOT - CLUSTER NODES + - CLUSTER SHARDS - HyperLogLog (complete) - PFADD - PFCOUNT @@ -301,11 +305,8 @@ Commands which will probably not be implemented: - ~~READONLY~~ - ~~READWRITE~~ - Key - - ~~DUMP~~ - ~~MIGRATE~~ - ~~OBJECT~~ - - ~~RESTORE~~ - - ~~WAIT~~ - Scripting - ~~FCALL / FCALL_RO *~~ - ~~FUNCTION *~~ @@ -329,7 +330,7 @@ Commands which will probably not be implemented: ## &c. -Integration tests are run against Redis 7.2.4. The [./integration](./integration/) subdir +Integration tests are run against Redis 8.4.0. The [./integration](./integration/) subdir compares miniredis against a real redis instance. The Redis 6 RESP3 protocol is supported. If there are problems, please open diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go b/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go index 9951f3dd3..4f7c77f42 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_cluster.go @@ -31,6 +31,8 @@ func (m *Miniredis) cmdCluster(c *server.Peer, cmd string, args []string) { m.cmdClusterKeySlot(c, cmd, args) case "NODES": m.cmdClusterNodes(c, cmd, args) + case "SHARDS": + m.cmdClusterShards(c, cmd, args) default: setDirty(c) c.WriteError(fmt.Sprintf("ERR 'CLUSTER %s' not supported", strings.Join(args, " "))) @@ -62,6 +64,59 @@ func (m *Miniredis) cmdClusterKeySlot(c *server.Peer, cmd string, args []string) // CLUSTER NODES func (m *Miniredis) cmdClusterNodes(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { - c.WriteBulk("e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca 127.0.0.1:7000@7000 myself,master - 0 0 1 connected 0-16383") + // do not try to use m.Addr() here, as m is blocked by this tx. + addr := m.srv.Addr() + port := m.srv.Addr().Port + c.WriteBulk(fmt.Sprintf("e7d1eecce10fd6bb5eb35b9f99a514335d9ba9ca %s@%d myself,master - 0 0 1 connected 0-16383", addr, port)) + }) +} + +// CLUSTER SHARDS +func (m *Miniredis) cmdClusterShards(c *server.Peer, cmd string, args []string) { + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + addr := m.srv.Addr() + host := addr.IP.String() + port := addr.Port + + // Array of shards (we return 1 shard) + c.WriteLen(1) + + // Shard is a map with 2 keys: "slots" and "nodes" + c.WriteMapLen(2) + + // "slots": flat list of start/end pairs (inclusive ranges) + c.WriteBulk("slots") + c.WriteLen(2) + c.WriteInt(0) + c.WriteInt(16383) + + // "nodes": array of node maps + c.WriteBulk("nodes") + c.WriteLen(1) + + // Node map. + // (id, endpoint, ip, port, role, replication-offset, health) + c.WriteMapLen(6) + + c.WriteBulk("id") + c.WriteBulk("13f84e686106847b76671957dd348fde540a77bb") + + //c.WriteBulk("endpoint") + //c.WriteBulk(host) // or host:port if your client expects that + + c.WriteBulk("ip") + c.WriteBulk(host) + + c.WriteBulk("port") + c.WriteInt(port) + + c.WriteBulk("role") + c.WriteBulk("master") + + c.WriteBulk("replication-offset") + c.WriteInt(0) + + c.WriteBulk("health") + c.WriteBulk("online") }) } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go b/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go index 1afb5cea1..b4ec55d7d 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_connection.go @@ -177,7 +177,7 @@ func (m *Miniredis) cmdHello(c *server.Peer, cmd string, args []string) { c.WriteBulk("server") c.WriteBulk("miniredis") c.WriteBulk("version") - c.WriteBulk("6.0.5") + c.WriteBulk("8.4.0") c.WriteBulk("proto") c.WriteInt(opts.version) c.WriteBulk("id") @@ -186,21 +186,22 @@ func (m *Miniredis) cmdHello(c *server.Peer, cmd string, args []string) { c.WriteBulk("standalone") c.WriteBulk("role") c.WriteBulk("master") - c.WriteBulk("modules") - c.WriteLen(0) + c.WriteBulk("modules") // "modules": [ + c.WriteLen(1) // we have 1: "vectorset" + c.WriteMapLen(4) // { + c.WriteBulk("name") // + c.WriteBulk("vectorset") // + c.WriteBulk("ver") // + c.WriteInt(1) // + c.WriteBulk("path") // + c.WriteBulk("") // + c.WriteBulk("args") // + c.WriteLen(0) // ]} end modules } // ECHO func (m *Miniredis) cmdEcho(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -213,12 +214,7 @@ func (m *Miniredis) cmdEcho(c *server.Peer, cmd string, args []string) { // SELECT func (m *Miniredis) cmdSelect(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.isValidCMD(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go index 721ad2fab..08c47a28f 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_generic.go @@ -32,30 +32,31 @@ func inMilliSeconds(t time.Time) int { func commandsGeneric(m *Miniredis) { m.srv.Register("COPY", m.cmdCopy) m.srv.Register("DEL", m.cmdDel) - // DUMP - m.srv.Register("EXISTS", m.cmdExists) + m.srv.Register("DUMP", m.cmdDump, server.ReadOnlyOption()) + m.srv.Register("EXISTS", m.cmdExists, server.ReadOnlyOption()) m.srv.Register("EXPIRE", makeCmdExpire(m, false, time.Second)) m.srv.Register("EXPIREAT", makeCmdExpire(m, true, time.Second)) - m.srv.Register("EXPIRETIME", m.makeCmdExpireTime(inSeconds)) - m.srv.Register("PEXPIRETIME", m.makeCmdExpireTime(inMilliSeconds)) - m.srv.Register("KEYS", m.cmdKeys) + m.srv.Register("EXPIRETIME", m.makeCmdExpireTime(inSeconds), server.ReadOnlyOption()) + m.srv.Register("PEXPIRETIME", m.makeCmdExpireTime(inMilliSeconds), server.ReadOnlyOption()) + m.srv.Register("KEYS", m.cmdKeys, server.ReadOnlyOption()) // MIGRATE m.srv.Register("MOVE", m.cmdMove) // OBJECT m.srv.Register("PERSIST", m.cmdPersist) m.srv.Register("PEXPIRE", makeCmdExpire(m, false, time.Millisecond)) m.srv.Register("PEXPIREAT", makeCmdExpire(m, true, time.Millisecond)) - m.srv.Register("PTTL", m.cmdPTTL) - m.srv.Register("RANDOMKEY", m.cmdRandomkey) + m.srv.Register("PTTL", m.cmdPTTL, server.ReadOnlyOption()) + m.srv.Register("RANDOMKEY", m.cmdRandomkey, server.ReadOnlyOption()) m.srv.Register("RENAME", m.cmdRename) m.srv.Register("RENAMENX", m.cmdRenamenx) - // RESTORE - m.srv.Register("TOUCH", m.cmdTouch) - m.srv.Register("TTL", m.cmdTTL) - m.srv.Register("TYPE", m.cmdType) - m.srv.Register("SCAN", m.cmdScan) + m.srv.Register("RESTORE", m.cmdRestore) + m.srv.Register("TOUCH", m.cmdTouch, server.ReadOnlyOption()) + m.srv.Register("TTL", m.cmdTTL, server.ReadOnlyOption()) + m.srv.Register("TYPE", m.cmdType, server.ReadOnlyOption()) + m.srv.Register("SCAN", m.cmdScan, server.ReadOnlyOption()) // SORT m.srv.Register("UNLINK", m.cmdDel) + m.srv.Register("WAIT", m.cmdWait) } type expireOpts struct { @@ -104,15 +105,7 @@ func expireParse(cmd string, args []string) (*expireOpts, error) { // converted to a duration. func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, string, []string) { return func(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -178,16 +171,7 @@ func makeCmdExpire(m *Miniredis, unix bool, d time.Duration) func(*server.Peer, // [pexpiretime]: https://redis.io/commands/pexpiretime/ func (m *Miniredis) makeCmdExpireTime(timeResultStrategy func(time.Time) int) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -213,16 +197,7 @@ func (m *Miniredis) makeCmdExpireTime(timeResultStrategy func(time.Time) int) se // TOUCH func (m *Miniredis) cmdTouch(c *server.Peer, cmd string, args []string) { - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } - - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -241,15 +216,7 @@ func (m *Miniredis) cmdTouch(c *server.Peer, cmd string, args []string) { // TTL func (m *Miniredis) cmdTTL(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -276,15 +243,7 @@ func (m *Miniredis) cmdTTL(c *server.Peer, cmd string, args []string) { // PTTL func (m *Miniredis) cmdPTTL(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -311,15 +270,7 @@ func (m *Miniredis) cmdPTTL(c *server.Peer, cmd string, args []string) { // PERSIST func (m *Miniredis) cmdPersist(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -347,16 +298,7 @@ func (m *Miniredis) cmdPersist(c *server.Peer, cmd string, args []string) { // DEL and UNLINK func (m *Miniredis) cmdDel(c *server.Peer, cmd string, args []string) { - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } - - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -374,17 +316,30 @@ func (m *Miniredis) cmdDel(c *server.Peer, cmd string, args []string) { }) } -// TYPE -func (m *Miniredis) cmdType(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError("usage error") - return - } - if !m.handleAuth(c) { +// DUMP +func (m *Miniredis) cmdDump(c *server.Peer, cmd string, args []string) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } - if m.checkPubsub(c, cmd) { + + key := args[0] + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + keyType, exists := db.keys[key] + if !exists { + c.WriteNull() + } else if keyType != keyTypeString { + c.WriteError(msgWrongType) + } else { + c.WriteBulk(db.stringGet(key)) + } + }) +} + +// TYPE +func (m *Miniredis) cmdType(c *server.Peer, cmd string, args []string) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -405,15 +360,7 @@ func (m *Miniredis) cmdType(c *server.Peer, cmd string, args []string) { // EXISTS func (m *Miniredis) cmdExists(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -432,15 +379,7 @@ func (m *Miniredis) cmdExists(c *server.Peer, cmd string, args []string) { // MOVE func (m *Miniredis) cmdMove(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -470,15 +409,7 @@ func (m *Miniredis) cmdMove(c *server.Peer, cmd string, args []string) { // KEYS func (m *Miniredis) cmdKeys(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -497,15 +428,7 @@ func (m *Miniredis) cmdKeys(c *server.Peer, cmd string, args []string) { // RANDOMKEY func (m *Miniredis) cmdRandomkey(c *server.Peer, cmd string, args []string) { - if len(args) != 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } @@ -529,15 +452,7 @@ func (m *Miniredis) cmdRandomkey(c *server.Peer, cmd string, args []string) { // RENAME func (m *Miniredis) cmdRename(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -564,15 +479,7 @@ func (m *Miniredis) cmdRename(c *server.Peer, cmd string, args []string) { // RENAMENX func (m *Miniredis) cmdRenamenx(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -602,6 +509,78 @@ func (m *Miniredis) cmdRenamenx(c *server.Peer, cmd string, args []string) { }) } +type restoreOpts struct { + key string + serializedValue string + rawTtl string + replace bool + absTtl bool +} + +func restoreParse(args []string) *restoreOpts { + var opts restoreOpts + + opts.key, opts.rawTtl, opts.serializedValue, args = args[0], args[1], args[2], args[3:] + + for len(args) > 0 { + switch arg := strings.ToUpper(args[0]); arg { + case "REPLACE": + opts.replace = true + case "ABSTTL": + opts.absTtl = true + default: + return nil + } + + args = args[1:] + } + + return &opts +} + +// RESTORE +func (m *Miniredis) cmdRestore(c *server.Peer, cmd string, args []string) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { + return + } + + var opts = restoreParse(args) + if opts == nil { + setDirty(c) + c.WriteError(msgSyntaxError) + return + } + + withTx(m, c, func(c *server.Peer, ctx *connCtx) { + db := m.db(ctx.selectedDB) + + _, keyExists := db.keys[opts.key] + if keyExists && !opts.replace { + setDirty(c) + c.WriteError("BUSYKEY Target key name already exists.") + return + } + + ttl, err := strconv.Atoi(opts.rawTtl) + if err != nil || ttl < 0 { + c.WriteError(msgInvalidInt) + return + } + + db.stringSet(opts.key, opts.serializedValue) + + if ttl != 0 { + if opts.absTtl { + db.ttl[opts.key] = m.at(ttl, time.Millisecond) + } else { + db.ttl[opts.key] = time.Duration(ttl) * time.Millisecond + } + } + + c.WriteOK() + }) +} + type scanOpts struct { cursor int count int @@ -658,15 +637,7 @@ func scanParse(cmd string, args []string) (*scanOpts, error) { // SCAN func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -679,7 +650,8 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) - // We return _all_ (matched) keys every time. + // We return _all_ (matched) keys every time, so that cursors work. + // We ignore "COUNT", which is allowed according to the Redis docs. var keys []string if opts.withType { @@ -700,25 +672,14 @@ func (m *Miniredis) cmdScan(c *server.Peer, cmd string, args []string) { keys, _ = matchKeys(keys, opts.match) } - low := opts.cursor - high := low + opts.count - // validate high is correct - if high > len(keys) || high == 0 { - high = len(keys) - } - if opts.cursor > high { - // invalid cursor + // we only ever return all at once, so no non-zero cursor can every be valid + if opts.cursor != 0 { c.WriteLen(2) c.WriteBulk("0") // no next cursor c.WriteLen(0) // no elements return } - cursorValue := low + opts.count - if cursorValue >= len(keys) { - cursorValue = 0 // no next cursor - } - keys = keys[low:high] - + cursorValue := 0 // we don't use cursors c.WriteLen(2) c.WriteBulk(fmt.Sprintf("%d", cursorValue)) c.WriteLen(len(keys)) @@ -766,15 +727,7 @@ func copyParse(cmd string, args []string) (*copyOpts, error) { // COPY func (m *Miniredis) cmdCopy(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -811,3 +764,26 @@ func (m *Miniredis) cmdCopy(c *server.Peer, cmd string, args []string) { c.WriteInt(1) }) } + +// WAIT +func (m *Miniredis) cmdWait(c *server.Peer, cmd string, args []string) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { + return + } + nReplicas, err := strconv.Atoi(args[0]) + if err != nil || nReplicas < 0 { + c.WriteError(msgInvalidInt) + return + } + timeout, err := strconv.Atoi(args[1]) + if err != nil { + c.WriteError(msgInvalidInt) + return + } + if timeout < 0 { + c.WriteError(msgTimeoutNegative) + return + } + // WAIT always returns 0 when called on a standalone instance + c.WriteInt(0) +} diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go index 97f74c3b1..12cf99add 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_geo.go @@ -14,27 +14,26 @@ import ( // commandsGeo handles GEOADD, GEORADIUS etc. func commandsGeo(m *Miniredis) { m.srv.Register("GEOADD", m.cmdGeoadd) - m.srv.Register("GEODIST", m.cmdGeodist) - m.srv.Register("GEOPOS", m.cmdGeopos) + m.srv.Register("GEODIST", m.cmdGeodist, server.ReadOnlyOption()) + m.srv.Register("GEOPOS", m.cmdGeopos, server.ReadOnlyOption()) m.srv.Register("GEORADIUS", m.cmdGeoradius) - m.srv.Register("GEORADIUS_RO", m.cmdGeoradius) + m.srv.Register("GEORADIUS_RO", m.cmdGeoradius, server.ReadOnlyOption()) m.srv.Register("GEORADIUSBYMEMBER", m.cmdGeoradiusbymember) - m.srv.Register("GEORADIUSBYMEMBER_RO", m.cmdGeoradiusbymember) + m.srv.Register("GEORADIUSBYMEMBER_RO", m.cmdGeoradiusbymember, server.ReadOnlyOption()) } // GEOADD func (m *Miniredis) cmdGeoadd(c *server.Peer, cmd string, args []string) { - if len(args) < 3 || len(args[1:])%3 != 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + + if len(args[1:])%3 != 0 { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) return } + key, args := args[0], args[1:] withTx(m, c, func(c *server.Peer, ctx *connCtx) { @@ -83,15 +82,7 @@ func (m *Miniredis) cmdGeoadd(c *server.Peer, cmd string, args []string) { // GEODIST func (m *Miniredis) cmdGeodist(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -141,17 +132,10 @@ func (m *Miniredis) cmdGeodist(c *server.Peer, cmd string, args []string) { // GEOPOS func (m *Miniredis) cmdGeopos(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } + key, args := args[0], args[1:] withTx(m, c, func(c *server.Peer, ctx *connCtx) { @@ -187,15 +171,7 @@ type geoDistance struct { // GEORADIUS and GEORADIUS_RO func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { - if len(args) < 5 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(5)) { return } @@ -374,15 +350,7 @@ func (m *Miniredis) cmdGeoradius(c *server.Peer, cmd string, args []string) { // GEORADIUSBYMEMBER and GEORADIUSBYMEMBER_RO func (m *Miniredis) cmdGeoradiusbymember(c *server.Peer, cmd string, args []string) { - if len(args) < 4 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(4)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go index 55332956f..29fec8229 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_hash.go @@ -13,34 +13,26 @@ import ( // commandsHash handles all hash value operations. func commandsHash(m *Miniredis) { m.srv.Register("HDEL", m.cmdHdel) - m.srv.Register("HEXISTS", m.cmdHexists) - m.srv.Register("HGET", m.cmdHget) - m.srv.Register("HGETALL", m.cmdHgetall) + m.srv.Register("HEXISTS", m.cmdHexists, server.ReadOnlyOption()) + m.srv.Register("HGET", m.cmdHget, server.ReadOnlyOption()) + m.srv.Register("HGETALL", m.cmdHgetall, server.ReadOnlyOption()) m.srv.Register("HINCRBY", m.cmdHincrby) m.srv.Register("HINCRBYFLOAT", m.cmdHincrbyfloat) - m.srv.Register("HKEYS", m.cmdHkeys) - m.srv.Register("HLEN", m.cmdHlen) - m.srv.Register("HMGET", m.cmdHmget) + m.srv.Register("HKEYS", m.cmdHkeys, server.ReadOnlyOption()) + m.srv.Register("HLEN", m.cmdHlen, server.ReadOnlyOption()) + m.srv.Register("HMGET", m.cmdHmget, server.ReadOnlyOption()) m.srv.Register("HMSET", m.cmdHmset) m.srv.Register("HSET", m.cmdHset) m.srv.Register("HSETNX", m.cmdHsetnx) - m.srv.Register("HSTRLEN", m.cmdHstrlen) - m.srv.Register("HVALS", m.cmdHvals) - m.srv.Register("HSCAN", m.cmdHscan) - m.srv.Register("HRANDFIELD", m.cmdHrandfield) + m.srv.Register("HSTRLEN", m.cmdHstrlen, server.ReadOnlyOption()) + m.srv.Register("HVALS", m.cmdHvals, server.ReadOnlyOption()) + m.srv.Register("HSCAN", m.cmdHscan, server.ReadOnlyOption()) + m.srv.Register("HRANDFIELD", m.cmdHrandfield, server.ReadOnlyOption()) } // HSET func (m *Miniredis) cmdHset(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -66,15 +58,7 @@ func (m *Miniredis) cmdHset(c *server.Peer, cmd string, args []string) { // HSETNX func (m *Miniredis) cmdHsetnx(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -113,15 +97,7 @@ func (m *Miniredis) cmdHsetnx(c *server.Peer, cmd string, args []string) { // HMSET func (m *Miniredis) cmdHmset(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -151,15 +127,7 @@ func (m *Miniredis) cmdHmset(c *server.Peer, cmd string, args []string) { // HGET func (m *Miniredis) cmdHget(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -188,15 +156,7 @@ func (m *Miniredis) cmdHget(c *server.Peer, cmd string, args []string) { // HDEL func (m *Miniredis) cmdHdel(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -242,15 +202,7 @@ func (m *Miniredis) cmdHdel(c *server.Peer, cmd string, args []string) { // HEXISTS func (m *Miniredis) cmdHexists(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -285,15 +237,7 @@ func (m *Miniredis) cmdHexists(c *server.Peer, cmd string, args []string) { // HGETALL func (m *Miniredis) cmdHgetall(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -322,15 +266,7 @@ func (m *Miniredis) cmdHgetall(c *server.Peer, cmd string, args []string) { // HKEYS func (m *Miniredis) cmdHkeys(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -358,15 +294,7 @@ func (m *Miniredis) cmdHkeys(c *server.Peer, cmd string, args []string) { // HSTRLEN func (m *Miniredis) cmdHstrlen(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -392,15 +320,7 @@ func (m *Miniredis) cmdHstrlen(c *server.Peer, cmd string, args []string) { // HVALS func (m *Miniredis) cmdHvals(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -429,15 +349,7 @@ func (m *Miniredis) cmdHvals(c *server.Peer, cmd string, args []string) { // HLEN func (m *Miniredis) cmdHlen(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -462,15 +374,7 @@ func (m *Miniredis) cmdHlen(c *server.Peer, cmd string, args []string) { // HMGET func (m *Miniredis) cmdHmget(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -503,15 +407,7 @@ func (m *Miniredis) cmdHmget(c *server.Peer, cmd string, args []string) { // HINCRBY func (m *Miniredis) cmdHincrby(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -546,15 +442,7 @@ func (m *Miniredis) cmdHincrby(c *server.Peer, cmd string, args []string) { // HINCRBYFLOAT func (m *Miniredis) cmdHincrbyfloat(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -593,15 +481,7 @@ func (m *Miniredis) cmdHincrbyfloat(c *server.Peer, cmd string, args []string) { // HSCAN func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -685,15 +565,7 @@ func (m *Miniredis) cmdHscan(c *server.Peer, cmd string, args []string) { // HRANDFIELD func (m *Miniredis) cmdHrandfield(c *server.Peer, cmd string, args []string) { - if len(args) > 3 || len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, between(1, 3)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go b/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go index ffb4d6f1b..7bfc9504d 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_hll.go @@ -5,21 +5,13 @@ import "github.com/alicebob/miniredis/v2/server" // commandsHll handles all hll related operations. func commandsHll(m *Miniredis) { m.srv.Register("PFADD", m.cmdPfadd) - m.srv.Register("PFCOUNT", m.cmdPfcount) + m.srv.Register("PFCOUNT", m.cmdPfcount, server.ReadOnlyOption()) m.srv.Register("PFMERGE", m.cmdPfmerge) } // PFADD func (m *Miniredis) cmdPfadd(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -40,15 +32,7 @@ func (m *Miniredis) cmdPfadd(c *server.Peer, cmd string, args []string) { // PFCOUNT func (m *Miniredis) cmdPfcount(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -69,15 +53,7 @@ func (m *Miniredis) cmdPfcount(c *server.Peer, cmd string, args []string) { // PFMERGE func (m *Miniredis) cmdPfmerge(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_info.go b/vendor/github.com/alicebob/miniredis/v2/cmd_info.go index e5984a9b2..9fa847112 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_info.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_info.go @@ -2,39 +2,42 @@ package miniredis import ( "fmt" + "strings" "github.com/alicebob/miniredis/v2/server" ) +const ( + clientsSectionName = "clients" + clientsSectionContent = "# Clients\nconnected_clients:%d\r\n" + + statsSectionName = "stats" + statsSectionContent = "# Stats\ntotal_connections_received:%d\r\ntotal_commands_processed:%d\r\n" +) + // Command 'INFO' from https://redis.io/commands/info/ func (m *Miniredis) cmdInfo(c *server.Peer, cmd string, args []string) { - if !m.isValidCMD(c, cmd) { + if !m.isValidCMD(c, cmd, args, between(0, 1)) { return } - - if len(args) > 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + var result string + if len(args) == 0 { + result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) + fmt.Sprintf(statsSectionContent, m.Server().TotalConnections(), m.Server().TotalCommands()) + c.WriteBulk(result) return } withTx(m, c, func(c *server.Peer, ctx *connCtx) { - const ( - clientsSectionName = "clients" - clientsSectionContent = "# Clients\nconnected_clients:%d\r\n" - ) - - var result string - - for _, key := range args { - if key != clientsSectionName { - setDirty(c) - c.WriteError(fmt.Sprintf("section (%s) is not supported", key)) - return - } + switch section := strings.ToLower(args[0]); section { + case clientsSectionName: + result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) + case statsSectionName: + result = fmt.Sprintf(statsSectionContent, m.Server().TotalConnections(), m.Server().TotalCommands()) + default: + setDirty(c) + c.WriteError(fmt.Sprintf("section (%s) is not supported", section)) + return } - result = fmt.Sprintf(clientsSectionContent, m.Server().ClientsLen()) - c.WriteBulk(result) }) } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go index 58199454a..9de16359d 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_list.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_list.go @@ -22,14 +22,14 @@ func commandsList(m *Miniredis) { m.srv.Register("BLPOP", m.cmdBlpop) m.srv.Register("BRPOP", m.cmdBrpop) m.srv.Register("BRPOPLPUSH", m.cmdBrpoplpush) - m.srv.Register("LINDEX", m.cmdLindex) - m.srv.Register("LPOS", m.cmdLpos) + m.srv.Register("LINDEX", m.cmdLindex, server.ReadOnlyOption()) + m.srv.Register("LPOS", m.cmdLpos, server.ReadOnlyOption()) m.srv.Register("LINSERT", m.cmdLinsert) - m.srv.Register("LLEN", m.cmdLlen) + m.srv.Register("LLEN", m.cmdLlen, server.ReadOnlyOption()) m.srv.Register("LPOP", m.cmdLpop) m.srv.Register("LPUSH", m.cmdLpush) m.srv.Register("LPUSHX", m.cmdLpushx) - m.srv.Register("LRANGE", m.cmdLrange) + m.srv.Register("LRANGE", m.cmdLrange, server.ReadOnlyOption()) m.srv.Register("LREM", m.cmdLrem) m.srv.Register("LSET", m.cmdLset) m.srv.Register("LTRIM", m.cmdLtrim) @@ -52,15 +52,7 @@ func (m *Miniredis) cmdBrpop(c *server.Peer, cmd string, args []string) { } func (m *Miniredis) cmdBXpop(c *server.Peer, cmd string, args []string, lr leftright) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -115,15 +107,7 @@ func (m *Miniredis) cmdBXpop(c *server.Peer, cmd string, args []string, lr leftr // LINDEX func (m *Miniredis) cmdLindex(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -164,16 +148,7 @@ func (m *Miniredis) cmdLindex(c *server.Peer, cmd string, args []string) { // LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len] func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) { - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } - - if len(args) == 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -311,15 +286,7 @@ func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) { // LINSERT func (m *Miniredis) cmdLinsert(c *server.Peer, cmd string, args []string) { - if len(args) != 4 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(4)) { return } @@ -378,15 +345,7 @@ func (m *Miniredis) cmdLinsert(c *server.Peer, cmd string, args []string) { // LLEN func (m *Miniredis) cmdLlen(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -421,15 +380,7 @@ func (m *Miniredis) cmdRpop(c *server.Peer, cmd string, args []string) { } func (m *Miniredis) cmdXpop(c *server.Peer, cmd string, args []string, lr leftright) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -513,15 +464,7 @@ func (m *Miniredis) cmdRpush(c *server.Peer, cmd string, args []string) { } func (m *Miniredis) cmdXpush(c *server.Peer, cmd string, args []string, lr leftright) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -559,15 +502,7 @@ func (m *Miniredis) cmdRpushx(c *server.Peer, cmd string, args []string) { } func (m *Miniredis) cmdXpushx(c *server.Peer, cmd string, args []string, lr leftright) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -600,15 +535,7 @@ func (m *Miniredis) cmdXpushx(c *server.Peer, cmd string, args []string, lr left // LRANGE func (m *Miniredis) cmdLrange(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -650,15 +577,7 @@ func (m *Miniredis) cmdLrange(c *server.Peer, cmd string, args []string) { // LREM func (m *Miniredis) cmdLrem(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -724,15 +643,7 @@ func (m *Miniredis) cmdLrem(c *server.Peer, cmd string, args []string) { // LSET func (m *Miniredis) cmdLset(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -777,15 +688,7 @@ func (m *Miniredis) cmdLset(c *server.Peer, cmd string, args []string) { // LTRIM func (m *Miniredis) cmdLtrim(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -831,15 +734,7 @@ func (m *Miniredis) cmdLtrim(c *server.Peer, cmd string, args []string) { // RPOPLPUSH func (m *Miniredis) cmdRpoplpush(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -864,15 +759,7 @@ func (m *Miniredis) cmdRpoplpush(c *server.Peer, cmd string, args []string) { // BRPOPLPUSH func (m *Miniredis) cmdBrpoplpush(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -918,15 +805,7 @@ func (m *Miniredis) cmdBrpoplpush(c *server.Peer, cmd string, args []string) { // LMOVE func (m *Miniredis) cmdLmove(c *server.Peer, cmd string, args []string) { - if len(args) != 4 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(4)) { return } @@ -979,15 +858,7 @@ func (m *Miniredis) cmdLmove(c *server.Peer, cmd string, args []string) { // BLMOVE func (m *Miniredis) cmdBlmove(c *server.Peer, cmd string, args []string) { - if len(args) != 5 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(5)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_object.go b/vendor/github.com/alicebob/miniredis/v2/cmd_object.go index b958a95cf..e8117c529 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_object.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_object.go @@ -14,15 +14,7 @@ func commandsObject(m *Miniredis) { // OBJECT func (m *Miniredis) cmdObject(c *server.Peer, cmd string, args []string) { - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_pubsub.go b/vendor/github.com/alicebob/miniredis/v2/cmd_pubsub.go index 0fc9f0de3..571431e8e 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_pubsub.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_pubsub.go @@ -173,15 +173,7 @@ func (m *Miniredis) cmdPunsubscribe(c *server.Peer, cmd string, args []string) { // PUBLISH func (m *Miniredis) cmdPublish(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_scripting.go b/vendor/github.com/alicebob/miniredis/v2/cmd_scripting.go index 188a15e97..32705b858 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_scripting.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_scripting.go @@ -18,7 +18,9 @@ import ( func commandsScripting(m *Miniredis) { m.srv.Register("EVAL", m.cmdEval) + m.srv.Register("EVAL_RO", m.cmdEvalro, server.ReadOnlyOption()) m.srv.Register("EVALSHA", m.cmdEvalsha) + m.srv.Register("EVALSHA_RO", m.cmdEvalshaRo, server.ReadOnlyOption()) m.srv.Register("SCRIPT", m.cmdScript) } @@ -28,7 +30,7 @@ var ( // Execute lua. Needs to run m.Lock()ed, from within withTx(). // Returns true if the lua was OK (and hence should be cached). -func (m *Miniredis) runLuaScript(c *server.Peer, sha, script string, args []string) bool { +func (m *Miniredis) runLuaScript(c *server.Peer, sha, script string, readOnly bool, args []string) bool { l := lua.NewState(lua.Options{SkipOpenLibs: true}) defer l.Close() @@ -85,7 +87,7 @@ func (m *Miniredis) runLuaScript(c *server.Peer, sha, script string, args []stri } l.SetGlobal("ARGV", argvTable) - redisFuncs, redisConstants := mkLua(m.srv, c, sha) + redisFuncs, redisConstants := mkLua(m.srv, c, sha, readOnly) // Register command handlers l.Push(l.NewFunction(func(l *lua.LState) int { mod := l.RegisterModule("redis", redisFuncs).(*lua.LTable) @@ -95,6 +97,7 @@ func (m *Miniredis) runLuaScript(c *server.Peer, sha, script string, args []stri l.Push(mod) return 1 })) + l.RegisterModule("os", mkLuaOS()) _ = doScript(l, protectGlobals) @@ -150,18 +153,12 @@ func compile(script string) (*lua.FunctionProto, error) { return proto, nil } -func (m *Miniredis) cmdEval(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { +// Shared implementation for EVAL and EVALRO +func (m *Miniredis) cmdEvalShared(c *server.Peer, cmd string, readOnly bool, args []string) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } + ctx := getCtx(c) if ctx.nested { c.WriteError(msgNotFromScripts(ctx.nestedSHA)) @@ -172,25 +169,24 @@ func (m *Miniredis) cmdEval(c *server.Peer, cmd string, args []string) { withTx(m, c, func(c *server.Peer, ctx *connCtx) { sha := sha1Hex(script) - ok := m.runLuaScript(c, sha, script, args) + ok := m.runLuaScript(c, sha, script, readOnly, args) if ok { m.scripts[sha] = script } }) } -func (m *Miniredis) cmdEvalsha(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { +// Wrapper function for EVAL command +func (m *Miniredis) cmdEval(c *server.Peer, cmd string, args []string) { + m.cmdEvalShared(c, cmd, false, args) +} + +// Shared implementation for EVALSHA and EVALSHA_RO +func (m *Miniredis) cmdEvalshaShared(c *server.Peer, cmd string, readOnly bool, args []string) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } + ctx := getCtx(c) if ctx.nested { c.WriteError(msgNotFromScripts(ctx.nestedSHA)) @@ -206,20 +202,27 @@ func (m *Miniredis) cmdEvalsha(c *server.Peer, cmd string, args []string) { return } - m.runLuaScript(c, sha, script, args) + m.runLuaScript(c, sha, script, readOnly, args) }) } +// Wrapper function for EVALSHA command +func (m *Miniredis) cmdEvalsha(c *server.Peer, cmd string, args []string) { + m.cmdEvalshaShared(c, cmd, false, args) +} + +// Wrapper function for EVALRO command +func (m *Miniredis) cmdEvalro(c *server.Peer, cmd string, args []string) { + m.cmdEvalShared(c, cmd, true, args) +} + +// Wrapper function for EVALSHA_RO command +func (m *Miniredis) cmdEvalshaRo(c *server.Peer, cmd string, args []string) { + m.cmdEvalshaShared(c, cmd, true, args) +} + func (m *Miniredis) cmdScript(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go index 5fe55dd4f..79e19fbc7 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_server.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_server.go @@ -13,7 +13,7 @@ import ( func commandsServer(m *Miniredis) { m.srv.Register("COMMAND", m.cmdCommand) - m.srv.Register("DBSIZE", m.cmdDbsize) + m.srv.Register("DBSIZE", m.cmdDbsize, server.ReadOnlyOption()) m.srv.Register("FLUSHALL", m.cmdFlushall) m.srv.Register("FLUSHDB", m.cmdFlushdb) m.srv.Register("INFO", m.cmdInfo) @@ -23,15 +23,7 @@ func commandsServer(m *Miniredis) { // MEMORY func (m *Miniredis) cmdMemory(c *server.Peer, cmd string, args []string) { - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -85,15 +77,7 @@ func (m *Miniredis) cmdMemory(c *server.Peer, cmd string, args []string) { // DBSIZE func (m *Miniredis) cmdDbsize(c *server.Peer, cmd string, args []string) { - if len(args) > 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } @@ -152,15 +136,7 @@ func (m *Miniredis) cmdFlushdb(c *server.Peer, cmd string, args []string) { // TIME func (m *Miniredis) cmdTime(c *server.Peer, cmd string, args []string) { - if len(args) > 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go index 12e4d5845..ec35d5636 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_set.go @@ -13,35 +13,27 @@ import ( // commandsSet handles all set value operations. func commandsSet(m *Miniredis) { m.srv.Register("SADD", m.cmdSadd) - m.srv.Register("SCARD", m.cmdScard) - m.srv.Register("SDIFF", m.cmdSdiff) + m.srv.Register("SCARD", m.cmdScard, server.ReadOnlyOption()) + m.srv.Register("SDIFF", m.cmdSdiff, server.ReadOnlyOption()) m.srv.Register("SDIFFSTORE", m.cmdSdiffstore) - m.srv.Register("SINTERCARD", m.cmdSintercard) - m.srv.Register("SINTER", m.cmdSinter) + m.srv.Register("SINTERCARD", m.cmdSintercard, server.ReadOnlyOption()) + m.srv.Register("SINTER", m.cmdSinter, server.ReadOnlyOption()) m.srv.Register("SINTERSTORE", m.cmdSinterstore) - m.srv.Register("SISMEMBER", m.cmdSismember) - m.srv.Register("SMEMBERS", m.cmdSmembers) - m.srv.Register("SMISMEMBER", m.cmdSmismember) + m.srv.Register("SISMEMBER", m.cmdSismember, server.ReadOnlyOption()) + m.srv.Register("SMEMBERS", m.cmdSmembers, server.ReadOnlyOption()) + m.srv.Register("SMISMEMBER", m.cmdSmismember, server.ReadOnlyOption()) m.srv.Register("SMOVE", m.cmdSmove) m.srv.Register("SPOP", m.cmdSpop) - m.srv.Register("SRANDMEMBER", m.cmdSrandmember) + m.srv.Register("SRANDMEMBER", m.cmdSrandmember, server.ReadOnlyOption()) m.srv.Register("SREM", m.cmdSrem) - m.srv.Register("SUNION", m.cmdSunion) + m.srv.Register("SUNION", m.cmdSunion, server.ReadOnlyOption()) m.srv.Register("SUNIONSTORE", m.cmdSunionstore) - m.srv.Register("SSCAN", m.cmdSscan) + m.srv.Register("SSCAN", m.cmdSscan, server.ReadOnlyOption()) } // SADD func (m *Miniredis) cmdSadd(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -62,15 +54,7 @@ func (m *Miniredis) cmdSadd(c *server.Peer, cmd string, args []string) { // SCARD func (m *Miniredis) cmdScard(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -96,15 +80,7 @@ func (m *Miniredis) cmdScard(c *server.Peer, cmd string, args []string) { // SDIFF func (m *Miniredis) cmdSdiff(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -128,15 +104,7 @@ func (m *Miniredis) cmdSdiff(c *server.Peer, cmd string, args []string) { // SDIFFSTORE func (m *Miniredis) cmdSdiffstore(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -159,15 +127,7 @@ func (m *Miniredis) cmdSdiffstore(c *server.Peer, cmd string, args []string) { // SINTER func (m *Miniredis) cmdSinter(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -191,15 +151,7 @@ func (m *Miniredis) cmdSinter(c *server.Peer, cmd string, args []string) { // SINTERSTORE func (m *Miniredis) cmdSinterstore(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -222,15 +174,7 @@ func (m *Miniredis) cmdSinterstore(c *server.Peer, cmd string, args []string) { // SINTERCARD func (m *Miniredis) cmdSintercard(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -293,15 +237,7 @@ func (m *Miniredis) cmdSintercard(c *server.Peer, cmd string, args []string) { // SISMEMBER func (m *Miniredis) cmdSismember(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -330,15 +266,7 @@ func (m *Miniredis) cmdSismember(c *server.Peer, cmd string, args []string) { // SMEMBERS func (m *Miniredis) cmdSmembers(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -368,15 +296,7 @@ func (m *Miniredis) cmdSmembers(c *server.Peer, cmd string, args []string) { // SMISMEMBER func (m *Miniredis) cmdSmismember(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -412,15 +332,7 @@ func (m *Miniredis) cmdSmismember(c *server.Peer, cmd string, args []string) { // SMOVE func (m *Miniredis) cmdSmove(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -456,15 +368,7 @@ func (m *Miniredis) cmdSmove(c *server.Peer, cmd string, args []string) { // SPOP func (m *Miniredis) cmdSpop(c *server.Peer, cmd string, args []string) { - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -547,22 +451,15 @@ func (m *Miniredis) cmdSpop(c *server.Peer, cmd string, args []string) { // SRANDMEMBER func (m *Miniredis) cmdSrandmember(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } + if len(args) > 2 { setDirty(c) c.WriteError(msgSyntaxError) return } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } key := args[0] count := 0 @@ -625,15 +522,7 @@ func (m *Miniredis) cmdSrandmember(c *server.Peer, cmd string, args []string) { // SREM func (m *Miniredis) cmdSrem(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -658,15 +547,7 @@ func (m *Miniredis) cmdSrem(c *server.Peer, cmd string, args []string) { // SUNION func (m *Miniredis) cmdSunion(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -690,15 +571,7 @@ func (m *Miniredis) cmdSunion(c *server.Peer, cmd string, args []string) { // SUNIONSTORE func (m *Miniredis) cmdSunionstore(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -721,15 +594,7 @@ func (m *Miniredis) cmdSunionstore(c *server.Peer, cmd string, args []string) { // SSCAN func (m *Miniredis) cmdSscan(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go b/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go index 85bc569f1..1491a74f4 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_sorted_set.go @@ -16,45 +16,37 @@ import ( // commandsSortedSet handles all sorted set operations. func commandsSortedSet(m *Miniredis) { m.srv.Register("ZADD", m.cmdZadd) - m.srv.Register("ZCARD", m.cmdZcard) - m.srv.Register("ZCOUNT", m.cmdZcount) + m.srv.Register("ZCARD", m.cmdZcard, server.ReadOnlyOption()) + m.srv.Register("ZCOUNT", m.cmdZcount, server.ReadOnlyOption()) m.srv.Register("ZINCRBY", m.cmdZincrby) - m.srv.Register("ZINTER", m.makeCmdZinter(false)) + m.srv.Register("ZINTER", m.makeCmdZinter(false), server.ReadOnlyOption()) m.srv.Register("ZINTERSTORE", m.makeCmdZinter(true)) - m.srv.Register("ZLEXCOUNT", m.cmdZlexcount) - m.srv.Register("ZRANGE", m.cmdZrange) - m.srv.Register("ZRANGEBYLEX", m.makeCmdZrangebylex(false)) - m.srv.Register("ZRANGEBYSCORE", m.makeCmdZrangebyscore(false)) - m.srv.Register("ZRANK", m.makeCmdZrank(false)) + m.srv.Register("ZLEXCOUNT", m.cmdZlexcount, server.ReadOnlyOption()) + m.srv.Register("ZRANGE", m.cmdZrange, server.ReadOnlyOption()) + m.srv.Register("ZRANGEBYLEX", m.makeCmdZrangebylex(false), server.ReadOnlyOption()) + m.srv.Register("ZRANGEBYSCORE", m.makeCmdZrangebyscore(false), server.ReadOnlyOption()) + m.srv.Register("ZRANK", m.makeCmdZrank(false), server.ReadOnlyOption()) m.srv.Register("ZREM", m.cmdZrem) m.srv.Register("ZREMRANGEBYLEX", m.cmdZremrangebylex) m.srv.Register("ZREMRANGEBYRANK", m.cmdZremrangebyrank) m.srv.Register("ZREMRANGEBYSCORE", m.cmdZremrangebyscore) - m.srv.Register("ZREVRANGE", m.cmdZrevrange) - m.srv.Register("ZREVRANGEBYLEX", m.makeCmdZrangebylex(true)) - m.srv.Register("ZREVRANGEBYSCORE", m.makeCmdZrangebyscore(true)) - m.srv.Register("ZREVRANK", m.makeCmdZrank(true)) - m.srv.Register("ZSCORE", m.cmdZscore) - m.srv.Register("ZMSCORE", m.cmdZMscore) - m.srv.Register("ZUNION", m.cmdZunion) + m.srv.Register("ZREVRANGE", m.cmdZrevrange, server.ReadOnlyOption()) + m.srv.Register("ZREVRANGEBYLEX", m.makeCmdZrangebylex(true), server.ReadOnlyOption()) + m.srv.Register("ZREVRANGEBYSCORE", m.makeCmdZrangebyscore(true), server.ReadOnlyOption()) + m.srv.Register("ZREVRANK", m.makeCmdZrank(true), server.ReadOnlyOption()) + m.srv.Register("ZSCORE", m.cmdZscore, server.ReadOnlyOption()) + m.srv.Register("ZMSCORE", m.cmdZMscore, server.ReadOnlyOption()) + m.srv.Register("ZUNION", m.cmdZunion, server.ReadOnlyOption()) m.srv.Register("ZUNIONSTORE", m.cmdZunionstore) - m.srv.Register("ZSCAN", m.cmdZscan) + m.srv.Register("ZSCAN", m.cmdZscan, server.ReadOnlyOption()) m.srv.Register("ZPOPMAX", m.cmdZpopmax(true)) m.srv.Register("ZPOPMIN", m.cmdZpopmax(false)) - m.srv.Register("ZRANDMEMBER", m.cmdZrandmember) + m.srv.Register("ZRANDMEMBER", m.cmdZrandmember, server.ReadOnlyOption()) } // ZADD func (m *Miniredis) cmdZadd(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -194,15 +186,7 @@ outer: // ZCARD func (m *Miniredis) cmdZcard(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -227,15 +211,7 @@ func (m *Miniredis) cmdZcard(c *server.Peer, cmd string, args []string) { // ZCOUNT func (m *Miniredis) cmdZcount(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -285,15 +261,7 @@ func (m *Miniredis) cmdZcount(c *server.Peer, cmd string, args []string) { // ZINCRBY func (m *Miniredis) cmdZincrby(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -332,15 +300,7 @@ func (m *Miniredis) makeCmdZinter(store bool) func(c *server.Peer, cmd string, a if store { minArgs++ } - if len(args) < minArgs { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(minArgs)) { return } @@ -513,15 +473,7 @@ func (m *Miniredis) makeCmdZinter(store bool) func(c *server.Peer, cmd string, a // ZLEXCOUNT func (m *Miniredis) cmdZlexcount(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -566,15 +518,7 @@ func (m *Miniredis) cmdZlexcount(c *server.Peer, cmd string, args []string) { // ZRANGE func (m *Miniredis) cmdZrange(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -668,15 +612,7 @@ func (m *Miniredis) cmdZrange(c *server.Peer, cmd string, args []string) { // ZREVRANGE func (m *Miniredis) cmdZrevrange(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -707,17 +643,10 @@ func (m *Miniredis) cmdZrevrange(c *server.Peer, cmd string, args []string) { // ZRANGEBYLEX and ZREVRANGEBYLEX func (m *Miniredis) makeCmdZrangebylex(reverse bool) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } + opts := optsRangeByLex{ Reverse: reverse, Key: args[0], @@ -756,15 +685,7 @@ func (m *Miniredis) makeCmdZrangebylex(reverse bool) server.Cmd { // ZRANGEBYSCORE and ZREVRANGEBYSCORE func (m *Miniredis) makeCmdZrangebyscore(reverse bool) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -808,15 +729,7 @@ func (m *Miniredis) makeCmdZrangebyscore(reverse bool) server.Cmd { // ZRANK and ZREVRANK func (m *Miniredis) makeCmdZrank(reverse bool) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -878,15 +791,7 @@ func (m *Miniredis) makeCmdZrank(reverse bool) server.Cmd { // ZREM func (m *Miniredis) cmdZrem(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -917,15 +822,7 @@ func (m *Miniredis) cmdZrem(c *server.Peer, cmd string, args []string) { // ZREMRANGEBYLEX func (m *Miniredis) cmdZremrangebylex(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -973,15 +870,7 @@ func (m *Miniredis) cmdZremrangebylex(c *server.Peer, cmd string, args []string) // ZREMRANGEBYRANK func (m *Miniredis) cmdZremrangebyrank(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -1023,15 +912,7 @@ func (m *Miniredis) cmdZremrangebyrank(c *server.Peer, cmd string, args []string // ZREMRANGEBYSCORE func (m *Miniredis) cmdZremrangebyscore(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -1084,15 +965,7 @@ func (m *Miniredis) cmdZremrangebyscore(c *server.Peer, cmd string, args []strin // ZSCORE func (m *Miniredis) cmdZscore(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -1122,15 +995,7 @@ func (m *Miniredis) cmdZscore(c *server.Peer, cmd string, args []string) { // ZMSCORE func (m *Miniredis) cmdZMscore(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -1271,16 +1136,7 @@ func withLexRange(members []string, min string, minIncl bool, max string, maxInc // ZUNION func (m *Miniredis) cmdZunion(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -1348,15 +1204,7 @@ func (m *Miniredis) cmdZunion(c *server.Peer, cmd string, args []string) { // ZUNIONSTORE func (m *Miniredis) cmdZunionstore(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -1508,15 +1356,7 @@ func executeZUnion(db *RedisDB, opts zunionOptions) (sortedSet, error) { // ZSCAN func (m *Miniredis) cmdZscan(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -1680,15 +1520,7 @@ func (m *Miniredis) cmdZpopmax(reverse bool) server.Cmd { // ZRANDMEMBER func (m *Miniredis) cmdZrandmember(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go index 7ce89d109..f2cccbb51 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_stream.go @@ -16,16 +16,16 @@ import ( // commandsStream handles all stream operations. func commandsStream(m *Miniredis) { m.srv.Register("XADD", m.cmdXadd) - m.srv.Register("XLEN", m.cmdXlen) - m.srv.Register("XREAD", m.cmdXread) - m.srv.Register("XRANGE", m.makeCmdXrange(false)) - m.srv.Register("XREVRANGE", m.makeCmdXrange(true)) + m.srv.Register("XLEN", m.cmdXlen, server.ReadOnlyOption()) + m.srv.Register("XREAD", m.cmdXread, server.ReadOnlyOption()) + m.srv.Register("XRANGE", m.makeCmdXrange(false), server.ReadOnlyOption()) + m.srv.Register("XREVRANGE", m.makeCmdXrange(true), server.ReadOnlyOption()) m.srv.Register("XGROUP", m.cmdXgroup) m.srv.Register("XINFO", m.cmdXinfo) m.srv.Register("XREADGROUP", m.cmdXreadgroup) m.srv.Register("XACK", m.cmdXack) m.srv.Register("XDEL", m.cmdXdel) - m.srv.Register("XPENDING", m.cmdXpending) + m.srv.Register("XPENDING", m.cmdXpending, server.ReadOnlyOption()) m.srv.Register("XTRIM", m.cmdXtrim) m.srv.Register("XAUTOCLAIM", m.cmdXautoclaim) m.srv.Register("XCLAIM", m.cmdXclaim) @@ -33,15 +33,7 @@ func commandsStream(m *Miniredis) { // XADD func (m *Miniredis) cmdXadd(c *server.Peer, cmd string, args []string) { - if len(args) < 4 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(4)) { return } @@ -137,15 +129,7 @@ func (m *Miniredis) cmdXadd(c *server.Peer, cmd string, args []string) { // XLEN func (m *Miniredis) cmdXlen(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -171,9 +155,7 @@ func (m *Miniredis) cmdXlen(c *server.Peer, cmd string, args []string) { // XRANGE and XREVRANGE func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { return func(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } if len(args) == 4 || len(args) > 5 { @@ -181,12 +163,6 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { c.WriteError(msgSyntaxError) return } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { - return - } opts := struct { key string @@ -320,15 +296,7 @@ func (m *Miniredis) makeCmdXrange(reverse bool) server.Cmd { // XGROUP func (m *Miniredis) cmdXgroup(c *server.Peer, cmd string, args []string) { - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -513,15 +481,7 @@ func (m *Miniredis) cmdXgroupDelconsumer(c *server.Peer, cmd string, args []stri // XINFO func (m *Miniredis) cmdXinfo(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -684,15 +644,7 @@ func (m *Miniredis) sinceMilli(t time.Time) int { // XREADGROUP func (m *Miniredis) cmdXreadgroup(c *server.Peer, cmd string, args []string) { // XREADGROUP GROUP group consumer STREAMS key ID - if len(args) < 6 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(6)) { return } @@ -801,12 +753,6 @@ parsing: c, opts.blockTimeout, func(c *server.Peer, ctx *connCtx) bool { - if ctx.nested { - setDirty(c) - c.WriteError("ERR XREADGROUP command is not allowed with BLOCK option from scripts") - return false - } - db := m.db(ctx.selectedDB) res, err := xreadgroup( db, @@ -871,15 +817,7 @@ func xreadgroup( // XACK func (m *Miniredis) cmdXack(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -908,15 +846,7 @@ func (m *Miniredis) cmdXack(c *server.Peer, cmd string, args []string) { // XDEL func (m *Miniredis) cmdXdel(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -946,15 +876,7 @@ func (m *Miniredis) cmdXdel(c *server.Peer, cmd string, args []string) { // XREAD func (m *Miniredis) cmdXread(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -1041,12 +963,6 @@ parsing: c, opts.blockTimeout, func(c *server.Peer, ctx *connCtx) bool { - if ctx.nested { - setDirty(c) - c.WriteError("ERR XREAD command is not allowed with BLOCK option from scripts") - return false - } - db := m.db(ctx.selectedDB) res := xread(db, opts.streams, opts.ids, opts.count) if len(res) == 0 { @@ -1128,15 +1044,7 @@ func writeXread(c *server.Peer, streams []string, res map[string][]StreamEntry) // XPENDING func (m *Miniredis) cmdXpending(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -1325,15 +1233,7 @@ func writeXpending( // XTRIM func (m *Miniredis) cmdXtrim(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -1431,15 +1331,7 @@ func (m *Miniredis) cmdXtrim(c *server.Peer, cmd string, args []string) { // XAUTOCLAIM func (m *Miniredis) cmdXautoclaim(c *server.Peer, cmd string, args []string) { // XAUTOCLAIM key group consumer min-idle-time start - if len(args) < 5 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(5)) { return } @@ -1596,15 +1488,7 @@ func writeXautoclaim(c *server.Peer, nextCallId string, res []StreamEntry, justI // XCLAIM func (m *Miniredis) cmdXclaim(c *server.Peer, cmd string, args []string) { - if len(args) < 5 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(5)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_string.go b/vendor/github.com/alicebob/miniredis/v2/cmd_string.go index 08e67746b..61c673270 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_string.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_string.go @@ -4,6 +4,7 @@ package miniredis import ( "math/big" + "math/bits" "strconv" "strings" "time" @@ -14,21 +15,21 @@ import ( // commandsString handles all string value operations. func commandsString(m *Miniredis) { m.srv.Register("APPEND", m.cmdAppend) - m.srv.Register("BITCOUNT", m.cmdBitcount) + m.srv.Register("BITCOUNT", m.cmdBitcount, server.ReadOnlyOption()) m.srv.Register("BITOP", m.cmdBitop) - m.srv.Register("BITPOS", m.cmdBitpos) + m.srv.Register("BITPOS", m.cmdBitpos, server.ReadOnlyOption()) m.srv.Register("DECRBY", m.cmdDecrby) m.srv.Register("DECR", m.cmdDecr) - m.srv.Register("GETBIT", m.cmdGetbit) - m.srv.Register("GET", m.cmdGet) + m.srv.Register("GETBIT", m.cmdGetbit, server.ReadOnlyOption()) + m.srv.Register("GET", m.cmdGet, server.ReadOnlyOption()) m.srv.Register("GETEX", m.cmdGetex) - m.srv.Register("GETRANGE", m.cmdGetrange) + m.srv.Register("GETRANGE", m.cmdGetrange, server.ReadOnlyOption()) m.srv.Register("GETSET", m.cmdGetset) m.srv.Register("GETDEL", m.cmdGetdel) m.srv.Register("INCRBYFLOAT", m.cmdIncrbyfloat) m.srv.Register("INCRBY", m.cmdIncrby) m.srv.Register("INCR", m.cmdIncr) - m.srv.Register("MGET", m.cmdMget) + m.srv.Register("MGET", m.cmdMget, server.ReadOnlyOption()) m.srv.Register("MSET", m.cmdMset) m.srv.Register("MSETNX", m.cmdMsetnx) m.srv.Register("PSETEX", m.cmdPsetex) @@ -37,20 +38,12 @@ func commandsString(m *Miniredis) { m.srv.Register("SET", m.cmdSet) m.srv.Register("SETNX", m.cmdSetnx) m.srv.Register("SETRANGE", m.cmdSetrange) - m.srv.Register("STRLEN", m.cmdStrlen) + m.srv.Register("STRLEN", m.cmdStrlen, server.ReadOnlyOption()) } // SET func (m *Miniredis) cmdSet(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -191,15 +184,7 @@ func (m *Miniredis) cmdSet(c *server.Peer, cmd string, args []string) { // SETEX func (m *Miniredis) cmdSetex(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -229,15 +214,7 @@ func (m *Miniredis) cmdSetex(c *server.Peer, cmd string, args []string) { // PSETEX func (m *Miniredis) cmdPsetex(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -270,15 +247,7 @@ func (m *Miniredis) cmdPsetex(c *server.Peer, cmd string, args []string) { // SETNX func (m *Miniredis) cmdSetnx(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -299,15 +268,7 @@ func (m *Miniredis) cmdSetnx(c *server.Peer, cmd string, args []string) { // MSET func (m *Miniredis) cmdMset(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -334,15 +295,7 @@ func (m *Miniredis) cmdMset(c *server.Peer, cmd string, args []string) { // MSETNX func (m *Miniredis) cmdMsetnx(c *server.Peer, cmd string, args []string) { - if len(args) < 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(2)) { return } @@ -382,15 +335,7 @@ func (m *Miniredis) cmdMsetnx(c *server.Peer, cmd string, args []string) { // GET func (m *Miniredis) cmdGet(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -414,15 +359,7 @@ func (m *Miniredis) cmdGet(c *server.Peer, cmd string, args []string) { // GETEX func (m *Miniredis) cmdGetex(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -501,15 +438,7 @@ func (m *Miniredis) cmdGetex(c *server.Peer, cmd string, args []string) { // GETSET func (m *Miniredis) cmdGetset(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -538,15 +467,7 @@ func (m *Miniredis) cmdGetset(c *server.Peer, cmd string, args []string) { // GETDEL func (m *Miniredis) cmdGetdel(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -573,15 +494,7 @@ func (m *Miniredis) cmdGetdel(c *server.Peer, cmd string, args []string) { // MGET func (m *Miniredis) cmdMget(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -607,15 +520,7 @@ func (m *Miniredis) cmdMget(c *server.Peer, cmd string, args []string) { // INCR func (m *Miniredis) cmdIncr(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -639,15 +544,7 @@ func (m *Miniredis) cmdIncr(c *server.Peer, cmd string, args []string) { // INCRBY func (m *Miniredis) cmdIncrby(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -680,15 +577,7 @@ func (m *Miniredis) cmdIncrby(c *server.Peer, cmd string, args []string) { // INCRBYFLOAT func (m *Miniredis) cmdIncrbyfloat(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -720,15 +609,7 @@ func (m *Miniredis) cmdIncrbyfloat(c *server.Peer, cmd string, args []string) { // DECR func (m *Miniredis) cmdDecr(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -752,15 +633,7 @@ func (m *Miniredis) cmdDecr(c *server.Peer, cmd string, args []string) { // DECRBY func (m *Miniredis) cmdDecrby(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -793,15 +666,7 @@ func (m *Miniredis) cmdDecrby(c *server.Peer, cmd string, args []string) { // STRLEN func (m *Miniredis) cmdStrlen(c *server.Peer, cmd string, args []string) { - if len(args) != 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(1)) { return } @@ -821,15 +686,7 @@ func (m *Miniredis) cmdStrlen(c *server.Peer, cmd string, args []string) { // APPEND func (m *Miniredis) cmdAppend(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -852,15 +709,7 @@ func (m *Miniredis) cmdAppend(c *server.Peer, cmd string, args []string) { // GETRANGE func (m *Miniredis) cmdGetrange(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -892,15 +741,7 @@ func (m *Miniredis) cmdGetrange(c *server.Peer, cmd string, args []string) { // SETRANGE func (m *Miniredis) cmdSetrange(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -943,15 +784,7 @@ func (m *Miniredis) cmdSetrange(c *server.Peer, cmd string, args []string) { // BITCOUNT func (m *Miniredis) cmdBitcount(c *server.Peer, cmd string, args []string) { - if len(args) < 1 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -972,6 +805,10 @@ func (m *Miniredis) cmdBitcount(c *server.Peer, cmd string, args []string) { } args = args[2:] } + if len(args) != 0 { + c.WriteError(msgSyntaxError) + return + } withTx(m, c, func(c *server.Peer, ctx *connCtx) { db := m.db(ctx.selectedDB) @@ -1002,15 +839,7 @@ func (m *Miniredis) cmdBitcount(c *server.Peer, cmd string, args []string) { // BITOP func (m *Miniredis) cmdBitop(c *server.Peer, cmd string, args []string) { - if len(args) < 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(3)) { return } @@ -1085,15 +914,7 @@ func (m *Miniredis) cmdBitop(c *server.Peer, cmd string, args []string) { // BITPOS func (m *Miniredis) cmdBitpos(c *server.Peer, cmd string, args []string) { - if len(args) < 2 || len(args) > 4 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, between(2, 4)) { return } @@ -1186,15 +1007,7 @@ func (m *Miniredis) cmdBitpos(c *server.Peer, cmd string, args []string) { // GETBIT func (m *Miniredis) cmdGetbit(c *server.Peer, cmd string, args []string) { - if len(args) != 2 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(2)) { return } @@ -1238,15 +1051,7 @@ func (m *Miniredis) cmdGetbit(c *server.Peer, cmd string, args []string) { // SETBIT func (m *Miniredis) cmdSetbit(c *server.Peer, cmd string, args []string) { - if len(args) != 3 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(3)) { return } @@ -1314,10 +1119,7 @@ func withRange(v string, start, end int) string { func countBits(v []byte) int { count := 0 for _, b := range []byte(v) { - for b > 0 { - count += int((b % uint8(2))) - b = b >> 1 - } + count += bits.OnesCount8(uint8(b)) } return count } diff --git a/vendor/github.com/alicebob/miniredis/v2/cmd_transactions.go b/vendor/github.com/alicebob/miniredis/v2/cmd_transactions.go index 94729e004..93d5354f7 100644 --- a/vendor/github.com/alicebob/miniredis/v2/cmd_transactions.go +++ b/vendor/github.com/alicebob/miniredis/v2/cmd_transactions.go @@ -17,14 +17,7 @@ func commandsTransaction(m *Miniredis) { // MULTI func (m *Miniredis) cmdMulti(c *server.Peer, cmd string, args []string) { - if len(args) != 0 { - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } @@ -45,15 +38,7 @@ func (m *Miniredis) cmdMulti(c *server.Peer, cmd string, args []string) { // EXEC func (m *Miniredis) cmdExec(c *server.Peer, cmd string, args []string) { - if len(args) != 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } @@ -99,15 +84,7 @@ func (m *Miniredis) cmdExec(c *server.Peer, cmd string, args []string) { // DISCARD func (m *Miniredis) cmdDiscard(c *server.Peer, cmd string, args []string) { - if len(args) != 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } @@ -123,15 +100,7 @@ func (m *Miniredis) cmdDiscard(c *server.Peer, cmd string, args []string) { // WATCH func (m *Miniredis) cmdWatch(c *server.Peer, cmd string, args []string) { - if len(args) == 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, atLeast(1)) { return } @@ -157,15 +126,7 @@ func (m *Miniredis) cmdWatch(c *server.Peer, cmd string, args []string) { // UNWATCH func (m *Miniredis) cmdUnwatch(c *server.Peer, cmd string, args []string) { - if len(args) != 0 { - setDirty(c) - c.WriteError(errWrongNumber(cmd)) - return - } - if !m.handleAuth(c) { - return - } - if m.checkPubsub(c, cmd) { + if !m.isValidCMD(c, cmd, args, exactly(0)) { return } diff --git a/vendor/github.com/alicebob/miniredis/v2/direct.go b/vendor/github.com/alicebob/miniredis/v2/direct.go index 88ef361e9..ff527b800 100644 --- a/vendor/github.com/alicebob/miniredis/v2/direct.go +++ b/vendor/github.com/alicebob/miniredis/v2/direct.go @@ -822,3 +822,41 @@ func (db *RedisDB) HllMerge(destKey string, sourceKeys ...string) error { func (m *Miniredis) Copy(srcDB int, src string, destDB int, dest string) error { return m.copy(m.DB(srcDB), src, m.DB(destDB), dest) } + +func (m *Miniredis) SCard(key string) (int, error) { + return m.DB(m.selectedDB).SCard(key) +} + +func (db *RedisDB) SCard(key string) (int, error) { + db.master.Lock() + defer db.master.Unlock() + if !db.exists(key) { + return 0, nil + } + if db.t(key) != "set" { + return 0, ErrWrongType + } + return len(db.setMembers(key)), nil +} + +// return "" if there were no members +func (m *Miniredis) SRandMember(key string) (string, error) { + return m.DB(m.selectedDB).SRandMember(key) +} + +func (db *RedisDB) SRandMember(key string) (string, error) { + db.master.Lock() + defer db.master.Unlock() + if !db.exists(key) { + return "", nil + } + if db.t(key) != "set" { + return "", ErrWrongType + } + members := db.setMembers(key) + if len(members) == 0 { + return "", nil + } + db.master.shuffle(members) + return members[0], nil +} diff --git a/vendor/github.com/alicebob/miniredis/v2/lua.go b/vendor/github.com/alicebob/miniredis/v2/lua.go index f62373921..29f3aeaec 100644 --- a/vendor/github.com/alicebob/miniredis/v2/lua.go +++ b/vendor/github.com/alicebob/miniredis/v2/lua.go @@ -18,7 +18,7 @@ var luaRedisConstants = map[string]lua.LValue{ "LOG_WARNING": lua.LNumber(3), } -func mkLua(srv *server.Server, c *server.Peer, sha string) (map[string]lua.LGFunction, map[string]lua.LValue) { +func mkLua(srv *server.Server, c *server.Peer, sha string, readOnly bool) (map[string]lua.LGFunction, map[string]lua.LValue) { mkCall := func(failFast bool) func(l *lua.LState) int { // one server.Ctx for a single Lua run pCtx := &connCtx{} @@ -52,6 +52,20 @@ func mkLua(srv *server.Server, c *server.Peer, sha string) (map[string]lua.LGFun return 0 } + if readOnly && len(args) > 0 { + if srv.IsRegisteredCommand(args[0]) && !srv.IsReadOnlyCommand(args[0]) { + if failFast { + l.Error(lua.LString("Write commands are not allowed in read-only scripts"), 1) + return 0 + } + // pcall() mode - return error table + res := &lua.LTable{} + res.RawSetString("err", lua.LString("Write commands are not allowed in read-only scripts")) + l.Push(res) + return 1 + } + } + buf := &bytes.Buffer{} wr := bufio.NewWriter(buf) peer := server.NewPeer(wr) @@ -71,7 +85,13 @@ func mkLua(srv *server.Server, c *server.Peer, sha string) (map[string]lua.LGFun return 0 } // pcall() mode - l.Push(lua.LNil) + res := &lua.LTable{} + if strings.Contains(err.Error(), "ERR unknown command") { + res.RawSetString("err", lua.LString("ERR Unknown Redis command called from script")) + } else { + res.RawSetString("err", lua.LString(err.Error())) + } + l.Push(res) return 1 } @@ -279,3 +299,14 @@ func luaStatusReply(msg string) *lua.LTable { tab.RawSetString("ok", lua.LString(msg)) return tab } + +// Our very minimal "os." lua lib. +func mkLuaOS() map[string]lua.LGFunction { + return map[string]lua.LGFunction{ + // > Returns an approximation of the amount in seconds of CPU time used by the program + "clock": func(l *lua.LState) int { + l.Push(lua.LNumber(42)) + return 1 + }, + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/miniredis.go b/vendor/github.com/alicebob/miniredis/v2/miniredis.go index 6996872c4..cc87474db 100644 --- a/vendor/github.com/alicebob/miniredis/v2/miniredis.go +++ b/vendor/github.com/alicebob/miniredis/v2/miniredis.go @@ -373,6 +373,14 @@ func (m *Miniredis) Server() *server.Server { return m.srv } +// IsReadOnlyCommand checks if a command is marked as read-only +func (m *Miniredis) IsReadOnlyCommand(cmd string) bool { + if m.srv == nil { + return false + } + return m.srv.IsReadOnlyCommand(cmd) +} + // Dump returns a text version of the selected DB, usable for debugging. // // Dump limits the maximum length of each key:value to "DumpMaxLineLen" characters. @@ -466,8 +474,31 @@ func (m *Miniredis) SetError(msg string) { m.srv.SetPreHook(cb) } +type argRequirements struct { + minimum int + maximum *int +} + +func atLeast(n int) argRequirements { + return argRequirements{n, nil} +} + +func between(a int, b int) argRequirements { + return argRequirements{a, &b} +} + +func exactly(n int) argRequirements { + return argRequirements{n, &n} +} + // isValidCMD returns true if command is valid and can be executed. -func (m *Miniredis) isValidCMD(c *server.Peer, cmd string) bool { +func (m *Miniredis) isValidCMD(c *server.Peer, cmd string, args []string, argReqs argRequirements) bool { + if len(args) < argReqs.minimum || (argReqs.maximum != nil && len(args) > *argReqs.maximum) { + setDirty(c) + c.WriteError(errWrongNumber(cmd)) + return false + } + if !m.handleAuth(c) { return false } diff --git a/vendor/github.com/alicebob/miniredis/v2/server/cmdmeta.go b/vendor/github.com/alicebob/miniredis/v2/server/cmdmeta.go new file mode 100644 index 000000000..a1be7e151 --- /dev/null +++ b/vendor/github.com/alicebob/miniredis/v2/server/cmdmeta.go @@ -0,0 +1,17 @@ +package server + +// cmdMeta holds metadata about a registered command +type cmdMeta struct { + handler Cmd + readOnly bool +} + +// CmdOption is a function that configures command metadata +type CmdOption func(*cmdMeta) + +// ReadOnlyOption marks a command as read-only +func ReadOnlyOption() CmdOption { + return func(meta *cmdMeta) { + meta.readOnly = true + } +} diff --git a/vendor/github.com/alicebob/miniredis/v2/server/server.go b/vendor/github.com/alicebob/miniredis/v2/server/server.go index b5f1b61ce..af36105f4 100644 --- a/vendor/github.com/alicebob/miniredis/v2/server/server.go +++ b/vendor/github.com/alicebob/miniredis/v2/server/server.go @@ -34,7 +34,7 @@ type Hook func(*Peer, string, ...string) bool // Server is a simple redis server type Server struct { l net.Listener - cmds map[string]Cmd + cmds map[string]*cmdMeta preHook Hook peers map[net.Conn]struct{} mu sync.Mutex @@ -62,7 +62,7 @@ func NewServerTLS(addr string, cfg *tls.Config) (*Server, error) { func newServer(l net.Listener) *Server { s := Server{ - cmds: map[string]Cmd{}, + cmds: map[string]*cmdMeta{}, peers: map[net.Conn]struct{}{}, l: l, } @@ -143,14 +143,23 @@ func (s *Server) Close() { // Register a command. It can't have been registered before. Safe to call on a // running server. -func (s *Server) Register(cmd string, f Cmd) error { +func (s *Server) Register(cmd string, f Cmd, options ...CmdOption) error { s.mu.Lock() defer s.mu.Unlock() cmd = strings.ToUpper(cmd) if _, ok := s.cmds[cmd]; ok { return fmt.Errorf("command already registered: %s", cmd) } - s.cmds[cmd] = f + + meta := &cmdMeta{ + handler: f, + readOnly: false, + } + for _, option := range options { + option(meta) + } + s.cmds[cmd] = meta + return nil } @@ -205,7 +214,7 @@ func (s *Server) Dispatch(c *Peer, args []string) { } s.mu.Lock() - cb, ok := s.cmds[cmdUp] + cmdMeta, ok := s.cmds[cmdUp] s.mu.Unlock() if !ok { c.WriteError(errUnknownCommand(cmd, args)) @@ -215,7 +224,7 @@ func (s *Server) Dispatch(c *Peer, args []string) { s.mu.Lock() s.infoCmds++ s.mu.Unlock() - cb(c, cmdUp, args) + cmdMeta.handler(c, cmdUp, args) if c.SwitchResp3 != nil { c.Resp3 = *c.SwitchResp3 c.SwitchResp3 = nil @@ -229,6 +238,26 @@ func (s *Server) TotalCommands() int { return s.infoCmds } +// IsRegisteredCommand checks if a command is registered +func (s *Server) IsRegisteredCommand(cmd string) bool { + s.mu.Lock() + defer s.mu.Unlock() + cmdUp := strings.ToUpper(cmd) + _, ok := s.cmds[cmdUp] + return ok +} + +// IsReadOnlyCommand checks if a command is marked as read-only +func (s *Server) IsReadOnlyCommand(cmd string) bool { + s.mu.Lock() + defer s.mu.Unlock() + cmdUp := strings.ToUpper(cmd) + if cmdMeta, ok := s.cmds[cmdUp]; ok { + return cmdMeta.readOnly + } + return false +} + // ClientsLen gives the number of connected clients right now func (s *Server) ClientsLen() int { s.mu.Lock() diff --git a/vendor/github.com/alicebob/miniredis/v2/stream.go b/vendor/github.com/alicebob/miniredis/v2/stream.go index f2dd466d5..27cc5209b 100644 --- a/vendor/github.com/alicebob/miniredis/v2/stream.go +++ b/vendor/github.com/alicebob/miniredis/v2/stream.go @@ -57,9 +57,7 @@ func newStreamKey() *streamKey { } // generateID doesn't lock the mutex -func (s *streamKey) generateID(now time.Time) string { - ts := uint64(now.UnixNano()) / 1_000_000 - +func (s *streamKey) generateID(ts uint64) string { next := fmt.Sprintf("%d-%d", ts, 0) if s.lastAllocatedID != "" && streamCmp(s.lastAllocatedID, next) >= 0 { last, _ := parseStreamID(s.lastAllocatedID) @@ -230,14 +228,23 @@ func (s *streamKey) createGroup(group, id string) error { } // streamAdd adds an entry to a stream. Returns the new entry ID. -// If id is empty or "*" the ID will be generated automatically. +// If id is empty, "*", or "123-*", the ID will be generated automatically. // `values` should have an even length. func (s *streamKey) add(entryID string, values []string, now time.Time) (string, error) { s.mu.Lock() defer s.mu.Unlock() - if entryID == "" || entryID == "*" { - entryID = s.generateID(now) + switch { + case entryID == "" || entryID == "*": + entryID = s.generateID(uint64(now.UnixMilli())) + default: + // "-*" + parts := strings.Split(entryID, "-") + if len(parts) == 2 && parts[1] == "*" { + if ts, err := strconv.ParseUint(parts[0], 10, 64); err == nil { + entryID = s.generateID(uint64(ts)) + } + } } entryID, err := formatStreamID(entryID) diff --git a/vendor/modules.txt b/vendor/modules.txt index aa6f0ffa0..4af568990 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -4,7 +4,7 @@ github.com/JaderDias/movingmedian # github.com/aclements/go-moremath v0.0.0-20210112150236-f10218a38794 ## explicit; go 1.12 github.com/aclements/go-moremath/mathx -# github.com/alicebob/miniredis/v2 v2.35.0 +# github.com/alicebob/miniredis/v2 v2.36.1 ## explicit; go 1.17 github.com/alicebob/miniredis/v2 github.com/alicebob/miniredis/v2/fpconv