// 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) }) }
// 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) }) }
// 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) } }) }
// 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-- } }) }
// 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) }) }
// 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) }) }
// 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) } }) }
// 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 }) }
// 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) }) }
// 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))) }) }
// 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) }) }
// 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)) }) }
// 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]) }) }
// 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]) } }) }
// SET func (m *Miniredis) cmdSet(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 } var ( nx = false // set iff not exists xx = false // set iff exists expire = 0 // For seconds and milliseconds. ) key := r.Args[0] value := r.Args[1] r.Args = r.Args[2:] for len(r.Args) > 0 { switch strings.ToUpper(r.Args[0]) { case "NX": nx = true r.Args = r.Args[1:] continue case "XX": xx = true r.Args = r.Args[1:] continue case "EX", "PX": if len(r.Args) < 2 { setDirty(r.Client()) out.WriteErrorString(msgInvalidInt) return nil } var err error expire, err = strconv.Atoi(r.Args[1]) if err != nil { setDirty(r.Client()) out.WriteErrorString(msgInvalidInt) return nil } r.Args = r.Args[2:] continue default: setDirty(r.Client()) out.WriteErrorString(msgSyntaxError) return nil } } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if nx { if db.exists(key) { out.WriteNil() return } } if xx { if !db.exists(key) { out.WriteNil() return } } db.del(key, true) // be sure to remove existing values of other type keys. // a vanilla SET clears the expire db.stringSet(key, value) if expire != 0 { db.expire[key] = expire } out.WriteOK() }) }