Ejemplo n.º 1
0
// SPOP
func (m *Miniredis) cmdSpop(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'spop' command")
		return nil
	}

	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteNil()
			return
		}

		if db.t(key) != "set" {
			out.WriteErrorString(ErrWrongType.Error())
			return
		}

		members := db.setMembers(key)
		member := members[rand.Intn(len(members))]
		db.setRem(key, member)
		out.WriteString(member)
	})
}
Ejemplo n.º 2
0
// HGET
func (m *Miniredis) cmdHget(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]
	field := r.Args[1]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		t, ok := db.keys[key]
		if !ok {
			out.WriteNil()
			return
		}
		if t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}
		value, ok := db.hashKeys[key][field]
		if !ok {
			out.WriteNil()
			return
		}
		out.WriteString(value)
	})
}
Ejemplo n.º 3
0
// RPOPLPUSH
func (m *Miniredis) cmdRpoplpush(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	src := r.Args[0]
	dst := r.Args[1]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(src) {
			out.WriteNil()
			return
		}
		if db.t(src) != "list" || (db.exists(dst) && db.t(dst) != "list") {
			out.WriteErrorString(msgWrongType)
			return
		}
		elem := db.listPop(src)
		db.listLpush(dst, elem)
		out.WriteString(elem)
	})
}
Ejemplo n.º 4
0
// HGETALL
func (m *Miniredis) cmdHgetall(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		t, ok := db.keys[key]
		if !ok {
			out.WriteBulkLen(0)
			return
		}
		if t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}

		out.WriteBulkLen(len(db.hashKeys[key]) * 2)
		for _, k := range db.hashFields(key) {
			out.WriteString(k)
			out.WriteString(db.hashGet(key, k))
		}
	})
}
Ejemplo n.º 5
0
// ZINCRBY
func (m *Miniredis) cmdZincrby(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 3 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]
	delta, err := strconv.ParseFloat(r.Args[1], 64)
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidFloat)
		return nil
	}
	member := r.Args[2]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if db.exists(key) && db.t(key) != "zset" {
			out.WriteErrorString(msgWrongType)
			return
		}
		newScore := db.ssetIncrby(key, member, delta)
		out.WriteString(formatFloat(newScore))
	})
}
Ejemplo n.º 6
0
// HMGET
func (m *Miniredis) cmdHmget(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 2 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hmget' command")
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}

		f, ok := db.hashKeys[key]
		if !ok {
			f = map[string]string{}
		}

		out.WriteBulkLen(len(r.Args) - 1)
		for _, k := range r.Args[1:] {
			v, ok := f[k]
			if !ok {
				out.WriteNil()
				continue
			}
			out.WriteString(v)
		}
	})
}
Ejemplo n.º 7
0
// SMEMBERS
func (m *Miniredis) cmdSmembers(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'smembers' command")
		return nil
	}

	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteBulkLen(0)
			return
		}

		if db.t(key) != "set" {
			out.WriteErrorString(ErrWrongType.Error())
			return
		}

		members := db.setMembers(key)

		out.WriteBulkLen(len(members))
		for _, elem := range members {
			out.WriteString(elem)
		}
	})
}
Ejemplo n.º 8
0
// HVALS
func (m *Miniredis) cmdHvals(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hvals' command")
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		t, ok := db.keys[key]
		if !ok {
			out.WriteBulkLen(0)
			return
		}
		if t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}

		out.WriteBulkLen(len(db.hashKeys[key]))
		for _, v := range db.hashKeys[key] {
			out.WriteString(v)
		}
	})
}
Ejemplo n.º 9
0
// GETRANGE
func (m *Miniredis) cmdGetrange(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 3 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]
	start, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}
	end, err := strconv.Atoi(r.Args[2])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "string" {
			out.WriteErrorString(msgWrongType)
			return
		}

		v := db.stringKeys[key]
		out.WriteString(withRange(v, start, end))
	})
}
Ejemplo n.º 10
0
// GETSET
func (m *Miniredis) cmdGetset(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	key := r.Args[0]
	value := r.Args[1]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "string" {
			out.WriteErrorString(msgWrongType)
			return
		}

		old, ok := db.stringKeys[key]
		db.stringSet(key, value)
		// a GETSET clears the expire
		delete(db.expire, key)

		if !ok {
			out.WriteNil()
			return
		}
		out.WriteString(old)
		return
	})
}
Ejemplo n.º 11
0
// MGET
func (m *Miniredis) cmdMget(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		out.WriteBulkLen(len(r.Args))
		for _, k := range r.Args {
			if t, ok := db.keys[k]; !ok || t != "string" {
				out.WriteNil()
				continue
			}
			v, ok := db.stringKeys[k]
			if !ok {
				// Should not happen, we just checked keys[]
				out.WriteNil()
				continue
			}
			out.WriteString(v)
		}
	})
}
Ejemplo n.º 12
0
// LPOP
func (m *Miniredis) cmdLpop(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'lpop' command")
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			// Non-existing key is fine.
			out.WriteNil()
			return
		}
		if db.t(key) != "list" {
			out.WriteErrorString(msgWrongType)
			return
		}

		elem := db.listLpop(key)
		out.WriteString(elem)
	})
}
Ejemplo n.º 13
0
// HINCRBYFLOAT
func (m *Miniredis) cmdHincrbyfloat(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 3 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hincrbyfloat' command")
		return nil
	}
	key := r.Args[0]
	field := r.Args[1]
	delta, err := strconv.ParseFloat(r.Args[2], 64)
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidFloat)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}

		v, err := db.hashIncrfloat(key, field, delta)
		if err != nil {
			out.WriteErrorString(err.Error())
			return
		}
		out.WriteString(formatFloat(v))
	})
}
Ejemplo n.º 14
0
// RPOP
func (m *Miniredis) cmdRpop(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteNil()
			return
		}
		if db.t(key) != "list" {
			out.WriteErrorString(msgWrongType)
			return
		}

		elem := db.listPop(key)
		out.WriteString(elem)
	})
}
Ejemplo n.º 15
0
// SDIFF
func (m *Miniredis) cmdSdiff(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	keys := r.Args

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		set, err := db.setDiff(keys)
		if err != nil {
			out.WriteErrorString(err.Error())
			return
		}

		out.WriteBulkLen(len(set))
		for k := range set {
			out.WriteString(k)
		}
	})
}
Ejemplo n.º 16
0
// ZSCORE
func (m *Miniredis) cmdZscore(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]
	member := r.Args[1]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteNil()
			return
		}

		if db.t(key) != "zset" {
			out.WriteErrorString(ErrWrongType.Error())
			return
		}

		if !db.ssetExists(key, member) {
			out.WriteNil()
			return
		}

		out.WriteString(formatFloat(db.ssetScore(key, member)))
	})
}
Ejemplo n.º 17
0
// HGET
func (m *Miniredis) cmdHget(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hget' command")
		return nil
	}
	key := r.Args[0]
	field := r.Args[1]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		t, ok := db.keys[key]
		if !ok {
			out.WriteNil()
			return
		}
		if t != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}
		value, ok := db.hashKeys[key][field]
		if !ok {
			out.WriteNil()
			return
		}
		out.WriteString(value)
	})
}
Ejemplo n.º 18
0
// INCRBYFLOAT
func (m *Miniredis) cmdIncrbyfloat(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]
	delta, err := strconv.ParseFloat(r.Args[1], 64)
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidFloat)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "string" {
			out.WriteErrorString(msgWrongType)
			return
		}

		v, err := db.stringIncrfloat(key, delta)
		if err != nil {
			out.WriteErrorString(err.Error())
			return
		}
		// Don't touch TTL
		out.WriteString(formatFloat(v))
	})
}
Ejemplo n.º 19
0
// HKEYS
func (m *Miniredis) cmdHkeys(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hkeys' command")
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteBulkLen(0)
			return
		}
		if db.t(key) != "hash" {
			out.WriteErrorString(msgWrongType)
			return
		}

		fields := db.hashFields(key)
		out.WriteBulkLen(len(fields))
		for _, f := range fields {
			out.WriteString(f)
		}
	})
}
Ejemplo n.º 20
0
// RANDOMKEY
func (m *Miniredis) cmdRandomkey(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 0 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if len(db.keys) == 0 {
			out.WriteNil()
			return
		}
		nr := rand.Intn(len(db.keys))
		for k := range db.keys {
			if nr == 0 {
				out.WriteString(k)
				return
			}
			nr--
		}
	})
}
Ejemplo n.º 21
0
// ECHO
func (m *Miniredis) cmdEcho(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("usage error")
		return nil
	}
	msg := r.Args[0]
	out.WriteString(msg)
	return nil
}
Ejemplo n.º 22
0
// ECHO
func (m *Redico) cmdEcho(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	msg := r.Args[0]
	out.WriteString(msg)
	return nil
}
Ejemplo n.º 23
0
// LRANGE
func (m *Miniredis) cmdLrange(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 3 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	key := r.Args[0]
	start, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}
	end, err := strconv.Atoi(r.Args[2])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if t, ok := db.keys[key]; ok && t != "list" {
			out.WriteErrorString(msgWrongType)
			return
		}

		l := db.listKeys[key]
		if len(l) == 0 {
			out.WriteBulkLen(0)
			return
		}

		rs, re := redisRange(len(l), start, end, false)
		out.WriteBulkLen(re - rs)
		for _, el := range l[rs:re] {
			out.WriteString(el)
		}
	})
}
Ejemplo n.º 24
0
// GET
func (m *Redico) cmdGet(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)
		if !db.exists(key) {
			out.WriteNil()
			return
		}
		out.WriteString(db.stringGet(key))
	})
}
Ejemplo n.º 25
0
// LINDEX
func (m *Miniredis) cmdLindex(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 2 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}
	key := r.Args[0]
	offset, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		t, ok := db.keys[key]
		if !ok {
			// No such key
			out.WriteNil()
			return
		}
		if t != "list" {
			out.WriteErrorString(msgWrongType)
			return
		}

		l := db.listKeys[key]
		if offset < 0 {
			offset = len(l) + offset
		}
		if offset < 0 || offset > len(l)-1 {
			out.WriteNil()
			return
		}
		out.WriteString(l[offset])
	})
}
Ejemplo n.º 26
0
// KEYSSTART
func (m *Redico) cmdKeysStart(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	key := r.Args[0]

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		keys := db.keyStart(key)
		out.WriteBulkLen(len(keys))
		for _, s := range keys {
			out.WriteString(s)
		}
	})
}
Ejemplo n.º 27
0
// SDIFF
func (m *Miniredis) cmdSdiff(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'sdiff' command")
		return nil
	}

	keys := r.Args

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		set, err := db.setDiff(keys)
		if err != nil {
			out.WriteErrorString(err.Error())
			return
		}

		out.WriteBulkLen(len(set))
		for k := range set {
			out.WriteString(k)
		}
	})
}
Ejemplo n.º 28
0
// SRANDMEMBER
func (m *Miniredis) cmdSrandmember(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 1 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'srandmember' command")
		return nil
	}
	if len(r.Args) > 2 {
		setDirty(r.Client())
		out.WriteErrorString(msgSyntaxError)
		return nil
	}

	key := r.Args[0]
	count := 0
	withCount := false
	if len(r.Args) == 2 {
		var err error
		count, err = strconv.Atoi(r.Args[1])
		if err != nil {
			setDirty(r.Client())
			out.WriteErrorString(msgInvalidInt)
			return nil
		}
		withCount = true
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		if !db.exists(key) {
			out.WriteNil()
			return
		}

		if db.t(key) != "set" {
			out.WriteErrorString(ErrWrongType.Error())
			return
		}

		members := db.setMembers(key)
		if count < 0 {
			// Non-unique elements is allowed with negative count.
			out.WriteBulkLen(-count)
			for count != 0 {
				member := members[rand.Intn(len(members))]
				out.WriteString(member)
				count++
			}
			return
		}

		// Must be unique elements.
		shuffle(members)
		if count > len(members) {
			count = len(members)
		}
		if !withCount {
			out.WriteString(members[0])
			return
		}
		out.WriteBulkLen(count)
		for i := range make([]struct{}, count) {
			out.WriteString(members[i])
		}
	})
}
Ejemplo n.º 29
0
// SCAN
func (m *Redico) cmdScan(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	cursor, err := strconv.Atoi(r.Args[0])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidCursor)
		return nil
	}
	// MATCH and COUNT options
	var withMatch bool
	var match string
	args := r.Args[1:]
	for len(args) > 0 {
		if strings.ToLower(args[0]) == "count" {
			if len(args) < 2 {
				setDirty(r.Client())
				out.WriteErrorString(msgSyntaxError)
				return nil
			}
			_, err := strconv.Atoi(args[1])
			if err != nil {
				setDirty(r.Client())
				out.WriteErrorString(msgInvalidInt)
				return nil
			}
			// We do nothing with count.
			args = args[2:]
			continue
		}
		if strings.ToLower(args[0]) == "match" {
			if len(args) < 2 {
				setDirty(r.Client())
				out.WriteErrorString(msgSyntaxError)
				return nil
			}
			withMatch = true
			match = args[1]
			args = args[2:]
			continue
		}
		setDirty(r.Client())
		out.WriteErrorString(msgSyntaxError)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)
		// We return _all_ (matched) keys every time.

		if cursor != 0 {
			// Invalid cursor.
			out.WriteBulkLen(2)
			out.WriteString("0") // no next cursor
			out.WriteBulkLen(0)  // no elements
			return
		}

		keys := db.allKeys()
		if withMatch {
			keys = matchKeys(keys, match)
		}

		out.WriteBulkLen(2)
		out.WriteString("0") // no next cursor
		out.WriteBulkLen(len(keys))
		for _, k := range keys {
			out.WriteString(k)
		}
	})
}
Ejemplo n.º 30
0
// HSCAN
func (m *Miniredis) cmdHscan(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 2 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hscan' command")
		return nil
	}

	key := r.Args[0]
	cursor, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidCursor)
		return nil
	}
	// MATCH and COUNT options
	var withMatch bool
	var match string
	args := r.Args[2:]
	for len(args) > 0 {
		if strings.ToLower(args[0]) == "count" {
			if len(args) < 2 {
				setDirty(r.Client())
				out.WriteErrorString(msgSyntaxError)
				return nil
			}
			_, err := strconv.Atoi(args[1])
			if err != nil {
				setDirty(r.Client())
				out.WriteErrorString(msgInvalidInt)
				return nil
			}
			// We do nothing with count.
			args = args[2:]
			continue
		}
		if strings.ToLower(args[0]) == "match" {
			if len(args) < 2 {
				setDirty(r.Client())
				out.WriteErrorString(msgSyntaxError)
				return nil
			}
			withMatch = true
			match = args[1]
			args = args[2:]
			continue
		}
		setDirty(r.Client())
		out.WriteErrorString(msgSyntaxError)
		return nil
	}

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)
		// We return _all_ (matched) keys every time.

		if cursor != 0 {
			// Invalid cursor.
			out.WriteBulkLen(2)
			out.WriteString("0") // no next cursor
			out.WriteBulkLen(0)  // no elements
			return
		}
		if db.exists(key) && db.t(key) != "hash" {
			out.WriteErrorString(ErrWrongType.Error())
			return
		}

		members := db.hashFields(key)
		if withMatch {
			members = matchKeys(members, match)
		}

		out.WriteBulkLen(2)
		out.WriteString("0") // no next cursor
		// HSCAN gives key, values.
		out.WriteBulkLen(len(members) * 2)
		for _, k := range members {
			out.WriteString(k)
			out.WriteString(db.hashGet(key, k))
		}
	})
}