示例#1
0
// MMSET
func (m *Miniredis) cmdHmset(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) < 3 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'hmset' command")
		return nil
	}
	key := r.Args[0]
	args := r.Args[1:]
	if len(args)%2 != 0 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for HMSET")
		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
		}

		for len(args) > 0 {
			field := args[0]
			value := args[1]
			args = args[2:]

			db.hashSet(key, field, value)
		}
		out.WriteOK()
	})
}
示例#2
0
// PSETEX
func (m *Miniredis) cmdPsetex(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]
	ttl, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}
	value := r.Args[2]

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

		db.del(key, true) // Clear any existing keys.
		db.stringSet(key, value)
		db.expire[key] = ttl // We put millisecond keys in with the second keys.
		out.WriteOK()
	})
}
示例#3
0
// MSET
func (m *Miniredis) cmdMset(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
	}
	if len(r.Args)%2 != 0 {
		setDirty(r.Client())
		// non-default error message
		out.WriteErrorString("ERR wrong number of arguments for MSET")
		return nil
	}
	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		db := m.db(ctx.selectedDB)

		for len(r.Args) > 0 {
			key := r.Args[0]
			value := r.Args[1]
			r.Args = r.Args[2:]

			db.del(key, true) // clear TTL
			db.stringSet(key, value)
		}
		out.WriteOK()
	})
}
示例#4
0
// FLUSHALL
func (m *Miniredis) cmdFlushall(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) {
		m.dbs = map[int]*RedisDB{}
		out.WriteOK()
	})
}
示例#5
0
// FLUSHDB
func (m *Miniredis) cmdFlushdb(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) {
		delete(m.dbs, ctx.selectedDB)
		out.WriteOK()
	})
}
示例#6
0
// UNWATCH
func (m *Miniredis) cmdUnwatch(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 0 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'unwatch' command")
		return nil
	}

	// Doesn't matter if UNWATCH is in a TX or not. Looks like a Redis bug to me.
	unwatch(getCtx(r.Client()))

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		// Do nothing if it's called in a transaction.
		out.WriteOK()
	})
}
示例#7
0
// MULTI
func (m *Miniredis) cmdMulti(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 0 {
		out.WriteErrorString("ERR wrong number of arguments for 'multi' command")
		return nil
	}
	ctx := getCtx(r.Client())

	if inTx(ctx) {
		out.WriteErrorString("ERR MULTI calls can not be nested")
		return nil
	}

	startTx(ctx)

	out.WriteOK()
	return nil
}
示例#8
0
// DISCARD
func (m *Miniredis) cmdDiscard(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 0 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'discard' command")
		return nil
	}

	ctx := getCtx(r.Client())
	if !inTx(ctx) {
		out.WriteErrorString("ERR DISCARD without MULTI")
		return nil
	}

	stopTx(ctx)
	out.WriteOK()
	return nil
}
示例#9
0
// UNWATCH
func (m *Miniredis) cmdUnwatch(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
	}

	// Doesn't matter if UNWATCH is in a TX or not. Looks like a Redis bug to me.
	unwatch(getCtx(r.Client()))

	return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) {
		// Do nothing if it's called in a transaction.
		out.WriteOK()
	})
}
示例#10
0
// DISCARD
func (m *Miniredis) cmdDiscard(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
	}

	ctx := getCtx(r.Client())
	if !inTx(ctx) {
		return redeo.ClientError("DISCARD without MULTI")
	}

	stopTx(ctx)
	out.WriteOK()
	return nil
}
示例#11
0
// MULTI
func (m *Miniredis) cmdMulti(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 0 {
		return r.WrongNumberOfArgs()
	}
	if !m.handleAuth(r.Client(), out) {
		return nil
	}

	ctx := getCtx(r.Client())

	if inTx(ctx) {
		return redeo.ClientError("MULTI calls can not be nested")
	}

	startTx(ctx)

	out.WriteOK()
	return nil
}
示例#12
0
// LSET
func (m *Miniredis) cmdLset(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]
	index, err := strconv.Atoi(r.Args[1])
	if err != nil {
		setDirty(r.Client())
		out.WriteErrorString(msgInvalidInt)
		return nil
	}
	value := r.Args[2]

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

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

		l := db.listKeys[key]
		if index < 0 {
			index = len(l) + index
		}
		if index < 0 || index > len(l)-1 {
			out.WriteErrorString(msgOutOfRange)
			return
		}
		l[index] = value
		db.keyVersion[key]++

		out.WriteOK()
	})
}
示例#13
0
// LTRIM
func (m *Miniredis) cmdLtrim(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)

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

		l := db.listKeys[key]
		rs, re := redisRange(len(l), start, end, false)
		db.listKeys[key] = l[rs:re]
		db.keyVersion[key]++
		out.WriteOK()
	})
}
示例#14
0
// SELECT
func (m *Miniredis) cmdSelect(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		out.WriteErrorString("usage error")
		return nil
	}
	id, err := strconv.Atoi(r.Args[0])
	if err != nil {
		id = 0
	}

	m.Lock()
	defer m.Unlock()

	ctx := getCtx(r.Client())
	ctx.selectedDB = id

	out.WriteOK()
	return nil
}
示例#15
0
// AUTH
func (m *Redico) cmdAuth(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) != 1 {
		setDirty(r.Client())
		return r.WrongNumberOfArgs()
	}
	pw := r.Args[0]

	m.Lock()
	defer m.Unlock()
	if m.password == "" {
		out.WriteErrorString("ERR Client sent AUTH, but no password is set")
		return nil
	}
	if m.password != pw {
		out.WriteErrorString("ERR invalid password")
		return nil
	}

	setAuthenticated(r.Client())
	out.WriteOK()
	return nil
}
示例#16
0
// WATCH
func (m *Miniredis) cmdWatch(out *redeo.Responder, r *redeo.Request) error {
	if len(r.Args) == 0 {
		setDirty(r.Client())
		out.WriteErrorString("ERR wrong number of arguments for 'watch' command")
		return nil
	}

	ctx := getCtx(r.Client())
	if inTx(ctx) {
		out.WriteErrorString("ERR WATCH in MULTI")
		return nil
	}

	m.Lock()
	defer m.Unlock()
	db := m.db(ctx.selectedDB)

	for _, key := range r.Args {
		watch(db, ctx, key)
	}
	out.WriteOK()
	return nil
}
示例#17
0
// SELECT
func (m *Redico) cmdSelect(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
	}

	id, err := strconv.Atoi(r.Args[0])
	if err != nil {
		id = 0
	}

	m.Lock()
	defer m.Unlock()

	ctx := getCtx(r.Client())
	ctx.selectedDB = id

	out.WriteOK()
	return nil
}
示例#18
0
// RENAME
func (m *Miniredis) cmdRename(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
	}

	from := r.Args[0]
	to := r.Args[1]

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

		if !db.exists(from) {
			out.WriteErrorString(msgKeyNotFound)
			return
		}

		db.rename(from, to)
		out.WriteOK()
	})
}
示例#19
0
// WATCH
func (m *Miniredis) cmdWatch(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
	}

	ctx := getCtx(r.Client())
	if inTx(ctx) {
		return redeo.ClientError("WATCH in MULTI")
	}

	m.Lock()
	defer m.Unlock()
	db := m.db(ctx.selectedDB)

	for _, key := range r.Args {
		watch(db, ctx, key)
	}
	out.WriteOK()
	return nil
}
示例#20
0
// AUTH
func (m *Miniredis) cmdAuth(out *redeo.Responder, r *redeo.Request) error {
	out.WriteOK()
	return nil
}
示例#21
0
// QUIT
func (m *Redico) cmdQuit(out *redeo.Responder, r *redeo.Request) error {
	// QUIT isn't transactionfied and accepts any arguments.
	out.WriteOK()
	r.Client().Close()
	return nil
}
示例#22
0
// 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()
	})
}