redis/command.go

3693 lines
69 KiB
Go
Raw Permalink Normal View History

2013-09-29 12:06:49 +04:00
package redis
import (
2020-03-11 17:26:42 +03:00
"context"
2013-09-29 12:06:49 +04:00
"fmt"
2018-08-02 14:48:46 +03:00
"net"
2013-09-29 12:06:49 +04:00
"strconv"
"time"
2023-01-23 09:48:54 +03:00
"github.com/redis/go-redis/v9/internal"
"github.com/redis/go-redis/v9/internal/hscan"
"github.com/redis/go-redis/v9/internal/proto"
"github.com/redis/go-redis/v9/internal/util"
2013-09-29 12:06:49 +04:00
)
type Cmder interface {
2017-05-09 12:44:36 +03:00
Name() string
2020-05-09 17:34:50 +03:00
FullName() string
2017-09-26 11:29:22 +03:00
Args() []interface{}
2020-02-14 15:30:07 +03:00
String() string
2017-09-26 11:29:22 +03:00
stringArg(int) string
2020-09-23 10:29:13 +03:00
firstKeyPos() int8
SetFirstKeyPos(int8)
2016-06-17 15:09:38 +03:00
2013-09-29 12:06:49 +04:00
readTimeout() *time.Duration
2019-08-24 11:55:13 +03:00
readReply(rd *proto.Reader) error
2013-09-29 12:06:49 +04:00
2020-02-03 12:53:47 +03:00
SetErr(error)
2013-09-29 12:06:49 +04:00
Err() error
}
2013-09-29 13:41:04 +04:00
func setCmdsErr(cmds []Cmder, e error) {
for _, cmd := range cmds {
2017-08-31 15:22:47 +03:00
if cmd.Err() == nil {
2020-02-03 12:53:47 +03:00
cmd.SetErr(e)
2017-08-31 15:22:47 +03:00
}
}
}
2018-08-12 11:11:01 +03:00
func cmdsFirstErr(cmds []Cmder) error {
2017-08-31 15:22:47 +03:00
for _, cmd := range cmds {
if err := cmd.Err(); err != nil {
return err
}
2013-09-29 13:41:04 +04:00
}
2017-08-31 15:22:47 +03:00
return nil
2013-09-29 13:41:04 +04:00
}
func writeCmds(wr *proto.Writer, cmds []Cmder) error {
for _, cmd := range cmds {
if err := writeCmd(wr, cmd); err != nil {
return err
}
}
2018-08-15 11:53:15 +03:00
return nil
}
func writeCmd(wr *proto.Writer, cmd Cmder) error {
return wr.WriteArgs(cmd.Args())
}
func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int {
2020-09-23 10:29:13 +03:00
if pos := cmd.firstKeyPos(); pos != 0 {
return int(pos)
}
2017-05-09 12:44:36 +03:00
switch cmd.Name() {
case "eval", "evalsha", "eval_ro", "evalsha_ro":
2017-09-26 11:29:22 +03:00
if cmd.stringArg(2) != "0" {
return 3
}
2018-01-24 21:38:47 +03:00
return 0
2017-04-12 13:00:20 +03:00
case "publish":
return 1
case "memory":
// https://github.com/redis/redis/issues/7493
if cmd.stringArg(1) == "usage" {
return 2
}
}
2020-09-23 10:29:13 +03:00
if info != nil {
return int(info.FirstKeyPos)
}
2022-06-04 16:07:28 +03:00
return 1
}
2020-05-09 17:30:16 +03:00
func cmdString(cmd Cmder, val interface{}) string {
2020-06-09 16:29:53 +03:00
b := make([]byte, 0, 64)
2020-05-09 17:30:16 +03:00
for i, arg := range cmd.Args() {
if i > 0 {
b = append(b, ' ')
}
2020-06-09 16:29:53 +03:00
b = internal.AppendArg(b, arg)
2020-05-09 17:30:16 +03:00
}
if err := cmd.Err(); err != nil {
b = append(b, ": "...)
b = append(b, err.Error()...)
} else if val != nil {
b = append(b, ": "...)
2020-06-09 16:29:53 +03:00
b = internal.AppendArg(b, val)
2020-05-09 17:30:16 +03:00
}
return util.BytesToString(b)
2020-05-09 17:30:16 +03:00
}
2013-09-29 12:06:49 +04:00
//------------------------------------------------------------------------------
type baseCmd struct {
2020-09-23 10:29:13 +03:00
ctx context.Context
args []interface{}
err error
keyPos int8
2013-09-29 12:06:49 +04:00
2016-03-08 18:18:52 +03:00
_readTimeout *time.Duration
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*Cmd)(nil)
func (cmd *baseCmd) Name() string {
2019-08-24 11:55:13 +03:00
if len(cmd.args) == 0 {
2019-08-12 14:53:00 +03:00
return ""
}
2019-08-12 14:53:00 +03:00
// Cmd name must be lower cased.
return internal.ToLower(cmd.stringArg(0))
2013-09-29 12:06:49 +04:00
}
2020-05-09 17:34:50 +03:00
func (cmd *baseCmd) FullName() string {
switch name := cmd.Name(); name {
case "cluster", "command":
if len(cmd.args) == 1 {
return name
}
if s2, ok := cmd.args[1].(string); ok {
return name + " " + s2
}
return name
default:
return name
}
}
2017-09-26 11:29:22 +03:00
func (cmd *baseCmd) Args() []interface{} {
2019-08-24 11:55:13 +03:00
return cmd.args
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
func (cmd *baseCmd) stringArg(pos int) string {
2019-08-24 11:55:13 +03:00
if pos < 0 || pos >= len(cmd.args) {
return ""
}
2022-02-18 06:36:04 +03:00
arg := cmd.args[pos]
switch v := arg.(type) {
case string:
return v
default:
2022-02-18 10:45:35 +03:00
// TODO: consider using appendArg
return fmt.Sprint(v)
2022-02-18 06:36:04 +03:00
}
}
2020-09-23 10:29:13 +03:00
func (cmd *baseCmd) firstKeyPos() int8 {
return cmd.keyPos
}
func (cmd *baseCmd) SetFirstKeyPos(keyPos int8) {
2020-09-23 10:29:13 +03:00
cmd.keyPos = keyPos
}
2020-02-03 12:53:47 +03:00
func (cmd *baseCmd) SetErr(e error) {
cmd.err = e
}
func (cmd *baseCmd) Err() error {
return cmd.err
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
func (cmd *baseCmd) readTimeout() *time.Duration {
return cmd._readTimeout
}
func (cmd *baseCmd) setReadTimeout(d time.Duration) {
cmd._readTimeout = &d
}
//------------------------------------------------------------------------------
type Cmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val interface{}
2013-09-29 12:06:49 +04:00
}
2020-03-11 17:26:42 +03:00
func NewCmd(ctx context.Context, args ...interface{}) *Cmd {
2016-06-17 15:09:38 +03:00
return &Cmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2016-06-17 15:09:38 +03:00
}
2013-09-29 12:06:49 +04:00
}
2020-02-14 15:30:07 +03:00
func (cmd *Cmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *Cmd) SetVal(val interface{}) {
cmd.val = val
}
2013-09-29 12:06:49 +04:00
func (cmd *Cmd) Val() interface{} {
return cmd.val
}
2014-01-09 12:17:38 +04:00
func (cmd *Cmd) Result() (interface{}, error) {
return cmd.val, cmd.err
}
2020-02-14 15:30:07 +03:00
func (cmd *Cmd) Text() (string, error) {
2018-08-12 11:11:01 +03:00
if cmd.err != nil {
return "", cmd.err
}
return toString(cmd.val)
}
func toString(val interface{}) (string, error) {
switch val := val.(type) {
2018-08-12 11:11:01 +03:00
case string:
return val, nil
default:
err := fmt.Errorf("redis: unexpected type=%T for String", val)
return "", err
}
}
2018-08-21 14:11:19 +03:00
func (cmd *Cmd) Int() (int, error) {
if cmd.err != nil {
return 0, cmd.err
}
switch val := cmd.val.(type) {
case int64:
return int(val), nil
case string:
return strconv.Atoi(val)
default:
2018-08-27 08:29:25 +03:00
err := fmt.Errorf("redis: unexpected type=%T for Int", val)
2018-08-21 14:11:19 +03:00
return 0, err
}
}
2018-08-12 11:11:01 +03:00
func (cmd *Cmd) Int64() (int64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return toInt64(cmd.val)
}
func toInt64(val interface{}) (int64, error) {
switch val := val.(type) {
2018-08-12 11:11:01 +03:00
case int64:
return val, nil
case string:
return strconv.ParseInt(val, 10, 64)
default:
err := fmt.Errorf("redis: unexpected type=%T for Int64", val)
return 0, err
}
}
func (cmd *Cmd) Uint64() (uint64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return toUint64(cmd.val)
}
func toUint64(val interface{}) (uint64, error) {
switch val := val.(type) {
2018-08-12 11:11:01 +03:00
case int64:
return uint64(val), nil
case string:
return strconv.ParseUint(val, 10, 64)
default:
err := fmt.Errorf("redis: unexpected type=%T for Uint64", val)
return 0, err
}
}
2019-04-10 15:27:06 +03:00
func (cmd *Cmd) Float32() (float32, error) {
if cmd.err != nil {
return 0, cmd.err
}
return toFloat32(cmd.val)
}
func toFloat32(val interface{}) (float32, error) {
switch val := val.(type) {
2019-04-10 15:27:06 +03:00
case int64:
return float32(val), nil
case string:
f, err := strconv.ParseFloat(val, 32)
if err != nil {
return 0, err
}
return float32(f), nil
default:
err := fmt.Errorf("redis: unexpected type=%T for Float32", val)
return 0, err
}
}
2018-08-12 11:11:01 +03:00
func (cmd *Cmd) Float64() (float64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return toFloat64(cmd.val)
}
func toFloat64(val interface{}) (float64, error) {
switch val := val.(type) {
2018-08-12 11:11:01 +03:00
case int64:
return float64(val), nil
case string:
return strconv.ParseFloat(val, 64)
default:
err := fmt.Errorf("redis: unexpected type=%T for Float64", val)
return 0, err
}
}
func (cmd *Cmd) Bool() (bool, error) {
if cmd.err != nil {
return false, cmd.err
}
return toBool(cmd.val)
}
func toBool(val interface{}) (bool, error) {
switch val := val.(type) {
2018-08-12 11:11:01 +03:00
case int64:
return val != 0, nil
case string:
return strconv.ParseBool(val)
default:
err := fmt.Errorf("redis: unexpected type=%T for Bool", val)
return false, err
}
2014-01-09 12:17:38 +04:00
}
func (cmd *Cmd) Slice() ([]interface{}, error) {
if cmd.err != nil {
return nil, cmd.err
}
switch val := cmd.val.(type) {
case []interface{}:
return val, nil
default:
return nil, fmt.Errorf("redis: unexpected type=%T for Slice", val)
}
}
func (cmd *Cmd) StringSlice() ([]string, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
ss := make([]string, len(slice))
for i, iface := range slice {
val, err := toString(iface)
if err != nil {
return nil, err
}
ss[i] = val
}
return ss, nil
}
func (cmd *Cmd) Int64Slice() ([]int64, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
nums := make([]int64, len(slice))
for i, iface := range slice {
val, err := toInt64(iface)
if err != nil {
return nil, err
}
nums[i] = val
}
return nums, nil
}
func (cmd *Cmd) Uint64Slice() ([]uint64, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
nums := make([]uint64, len(slice))
for i, iface := range slice {
val, err := toUint64(iface)
if err != nil {
return nil, err
}
nums[i] = val
}
return nums, nil
}
func (cmd *Cmd) Float32Slice() ([]float32, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
floats := make([]float32, len(slice))
for i, iface := range slice {
val, err := toFloat32(iface)
if err != nil {
return nil, err
}
floats[i] = val
}
return floats, nil
}
func (cmd *Cmd) Float64Slice() ([]float64, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
floats := make([]float64, len(slice))
for i, iface := range slice {
val, err := toFloat64(iface)
if err != nil {
return nil, err
}
floats[i] = val
}
return floats, nil
}
func (cmd *Cmd) BoolSlice() ([]bool, error) {
slice, err := cmd.Slice()
if err != nil {
return nil, err
}
bools := make([]bool, len(slice))
for i, iface := range slice {
val, err := toBool(iface)
if err != nil {
return nil, err
}
bools[i] = val
}
return bools, nil
}
2020-09-17 12:27:16 +03:00
func (cmd *Cmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadReply()
2020-09-17 12:27:16 +03:00
return err
2014-01-09 12:17:38 +04:00
}
//------------------------------------------------------------------------------
type SliceCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val []interface{}
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*SliceCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewSliceCmd(ctx context.Context, args ...interface{}) *SliceCmd {
2017-01-13 14:39:59 +03:00
return &SliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2014-01-09 12:17:38 +04:00
}
func (cmd *SliceCmd) SetVal(val []interface{}) {
cmd.val = val
}
2014-01-09 12:17:38 +04:00
func (cmd *SliceCmd) Val() []interface{} {
return cmd.val
}
func (cmd *SliceCmd) Result() ([]interface{}, error) {
return cmd.val, cmd.err
}
func (cmd *SliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
// Scan scans the results from the map into a destination struct. The map keys
// are matched in the Redis struct fields by the `redis:"field"` tag.
func (cmd *SliceCmd) Scan(dst interface{}) error {
if cmd.err != nil {
return cmd.err
}
// Pass the list of keys and values.
// Skip the first two args for: HMGET key
var args []interface{}
if cmd.args[0] == "hmget" {
args = cmd.args[2:]
} else {
// Otherwise, it's: MGET field field ...
args = cmd.args[1:]
}
return hscan.Scan(dst, args, cmd.val)
}
func (cmd *SliceCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadSlice()
return err
2014-01-09 12:17:38 +04:00
}
2013-09-29 12:06:49 +04:00
//------------------------------------------------------------------------------
type StatusCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val string
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*StatusCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewStatusCmd(ctx context.Context, args ...interface{}) *StatusCmd {
2017-01-13 14:39:59 +03:00
return &StatusCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2015-01-24 15:12:48 +03:00
}
func (cmd *StatusCmd) SetVal(val string) {
cmd.val = val
}
2013-09-29 12:06:49 +04:00
func (cmd *StatusCmd) Val() string {
2014-01-09 12:17:38 +04:00
return cmd.val
}
func (cmd *StatusCmd) Result() (string, error) {
return cmd.val, cmd.err
}
func (cmd *StatusCmd) String() string {
return cmdString(cmd, cmd.val)
}
2020-09-17 12:27:16 +03:00
func (cmd *StatusCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadString()
return err
2013-09-29 12:06:49 +04:00
}
//------------------------------------------------------------------------------
type IntCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val int64
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*IntCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewIntCmd(ctx context.Context, args ...interface{}) *IntCmd {
2017-01-13 14:39:59 +03:00
return &IntCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *IntCmd) SetVal(val int64) {
cmd.val = val
}
2013-09-29 12:06:49 +04:00
func (cmd *IntCmd) Val() int64 {
2014-01-09 12:17:38 +04:00
return cmd.val
}
func (cmd *IntCmd) Result() (int64, error) {
return cmd.val, cmd.err
}
2019-09-27 14:38:55 +03:00
func (cmd *IntCmd) Uint64() (uint64, error) {
return uint64(cmd.val), cmd.err
}
2014-01-09 12:17:38 +04:00
func (cmd *IntCmd) String() string {
return cmdString(cmd, cmd.val)
}
2020-09-17 12:27:16 +03:00
func (cmd *IntCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadInt()
2020-09-17 12:27:16 +03:00
return err
2013-09-29 12:06:49 +04:00
}
//------------------------------------------------------------------------------
2019-06-26 14:45:38 +03:00
type IntSliceCmd struct {
baseCmd
val []int64
}
var _ Cmder = (*IntSliceCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewIntSliceCmd(ctx context.Context, args ...interface{}) *IntSliceCmd {
2019-06-26 14:45:38 +03:00
return &IntSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2019-06-26 14:45:38 +03:00
}
}
func (cmd *IntSliceCmd) SetVal(val []int64) {
cmd.val = val
}
2019-06-26 14:45:38 +03:00
func (cmd *IntSliceCmd) Val() []int64 {
return cmd.val
}
func (cmd *IntSliceCmd) Result() ([]int64, error) {
return cmd.val, cmd.err
}
func (cmd *IntSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]int64, n)
for i := 0; i < len(cmd.val); i++ {
if cmd.val[i], err = rd.ReadInt(); err != nil {
return err
2019-06-26 14:45:38 +03:00
}
}
return nil
2019-06-26 14:45:38 +03:00
}
//------------------------------------------------------------------------------
type DurationCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val time.Duration
precision time.Duration
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*DurationCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewDurationCmd(ctx context.Context, precision time.Duration, args ...interface{}) *DurationCmd {
return &DurationCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
precision: precision,
}
}
func (cmd *DurationCmd) SetVal(val time.Duration) {
cmd.val = val
}
2014-01-09 12:17:38 +04:00
func (cmd *DurationCmd) Val() time.Duration {
return cmd.val
}
2014-01-09 12:17:38 +04:00
func (cmd *DurationCmd) Result() (time.Duration, error) {
return cmd.val, cmd.err
}
func (cmd *DurationCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *DurationCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadInt()
2020-09-17 12:27:16 +03:00
if err != nil {
return err
}
2019-05-31 13:16:10 +03:00
switch n {
// -2 if the key does not exist
// -1 if the key exists but has no associated expire
case -2, -1:
cmd.val = time.Duration(n)
default:
cmd.val = time.Duration(n) * cmd.precision
}
2014-01-09 12:17:38 +04:00
return nil
}
//------------------------------------------------------------------------------
2016-10-14 14:39:02 +03:00
type TimeCmd struct {
baseCmd
val time.Time
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*TimeCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewTimeCmd(ctx context.Context, args ...interface{}) *TimeCmd {
2016-10-14 14:39:02 +03:00
return &TimeCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2016-10-14 14:39:02 +03:00
}
}
func (cmd *TimeCmd) SetVal(val time.Time) {
cmd.val = val
}
2016-10-14 14:39:02 +03:00
func (cmd *TimeCmd) Val() time.Time {
return cmd.val
}
func (cmd *TimeCmd) Result() (time.Time, error) {
return cmd.val, cmd.err
}
func (cmd *TimeCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *TimeCmd) readReply(rd *proto.Reader) error {
if err := rd.ReadFixedArrayLen(2); err != nil {
return err
}
second, err := rd.ReadInt()
if err != nil {
return err
}
microsecond, err := rd.ReadInt()
if err != nil {
return err
}
cmd.val = time.Unix(second, microsecond*1000)
return nil
2018-08-02 14:48:46 +03:00
}
2016-10-14 14:39:02 +03:00
//------------------------------------------------------------------------------
2013-09-29 12:06:49 +04:00
type BoolCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val bool
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*BoolCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewBoolCmd(ctx context.Context, args ...interface{}) *BoolCmd {
2017-01-13 14:39:59 +03:00
return &BoolCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *BoolCmd) SetVal(val bool) {
cmd.val = val
}
2014-01-09 12:17:38 +04:00
func (cmd *BoolCmd) Val() bool {
return cmd.val
2013-09-29 12:06:49 +04:00
}
2014-01-09 12:17:38 +04:00
func (cmd *BoolCmd) Result() (bool, error) {
return cmd.val, cmd.err
}
func (cmd *BoolCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *BoolCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadBool()
2015-10-07 17:56:49 +03:00
// `SET key value NX` returns nil when key already exists. But
// `SETNX key value` returns bool (0/1). So convert nil to bool.
2020-09-17 12:27:16 +03:00
if err == Nil {
cmd.val = false
err = nil
}
return err
2013-09-29 12:06:49 +04:00
}
//------------------------------------------------------------------------------
type StringCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
2018-08-16 13:25:19 +03:00
val string
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*StringCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewStringCmd(ctx context.Context, args ...interface{}) *StringCmd {
2017-01-13 14:39:59 +03:00
return &StringCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *StringCmd) SetVal(val string) {
cmd.val = val
}
2013-09-29 12:06:49 +04:00
func (cmd *StringCmd) Val() string {
2018-08-16 13:25:19 +03:00
return cmd.val
2014-01-09 12:17:38 +04:00
}
func (cmd *StringCmd) Result() (string, error) {
return cmd.Val(), cmd.err
}
func (cmd *StringCmd) Bytes() ([]byte, error) {
return util.StringToBytes(cmd.val), cmd.err
2014-01-09 12:17:38 +04:00
}
2021-03-17 10:50:02 +03:00
func (cmd *StringCmd) Bool() (bool, error) {
if cmd.err != nil {
return false, cmd.err
}
return strconv.ParseBool(cmd.val)
}
2018-08-21 14:11:19 +03:00
func (cmd *StringCmd) Int() (int, error) {
if cmd.err != nil {
return 0, cmd.err
}
return strconv.Atoi(cmd.Val())
}
2014-07-31 16:18:23 +04:00
func (cmd *StringCmd) Int64() (int64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return strconv.ParseInt(cmd.Val(), 10, 64)
2014-07-31 16:18:23 +04:00
}
func (cmd *StringCmd) Uint64() (uint64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return strconv.ParseUint(cmd.Val(), 10, 64)
2014-07-31 16:18:23 +04:00
}
2019-04-10 15:27:06 +03:00
func (cmd *StringCmd) Float32() (float32, error) {
if cmd.err != nil {
return 0, cmd.err
}
f, err := strconv.ParseFloat(cmd.Val(), 32)
if err != nil {
return 0, err
}
return float32(f), nil
}
2014-07-31 16:18:23 +04:00
func (cmd *StringCmd) Float64() (float64, error) {
if cmd.err != nil {
return 0, cmd.err
}
return strconv.ParseFloat(cmd.Val(), 64)
}
func (cmd *StringCmd) Time() (time.Time, error) {
if cmd.err != nil {
return time.Time{}, cmd.err
}
return time.Parse(time.RFC3339Nano, cmd.Val())
}
func (cmd *StringCmd) Scan(val interface{}) error {
if cmd.err != nil {
return cmd.err
}
2018-08-16 13:25:19 +03:00
return proto.Scan([]byte(cmd.val), val)
2014-07-31 16:18:23 +04:00
}
2014-01-09 12:17:38 +04:00
func (cmd *StringCmd) String() string {
return cmdString(cmd, cmd.val)
}
2020-09-17 12:27:16 +03:00
func (cmd *StringCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadString()
return err
2013-09-29 12:06:49 +04:00
}
//------------------------------------------------------------------------------
type FloatCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val float64
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*FloatCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewFloatCmd(ctx context.Context, args ...interface{}) *FloatCmd {
2017-01-13 14:39:59 +03:00
return &FloatCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *FloatCmd) SetVal(val float64) {
cmd.val = val
}
2013-09-29 12:06:49 +04:00
func (cmd *FloatCmd) Val() float64 {
2014-01-09 12:17:38 +04:00
return cmd.val
2013-09-29 12:06:49 +04:00
}
2015-08-29 13:08:27 +03:00
func (cmd *FloatCmd) Result() (float64, error) {
return cmd.Val(), cmd.Err()
}
2014-01-09 12:17:38 +04:00
func (cmd *FloatCmd) String() string {
return cmdString(cmd, cmd.val)
2013-09-29 12:06:49 +04:00
}
2020-09-17 12:27:16 +03:00
func (cmd *FloatCmd) readReply(rd *proto.Reader) (err error) {
cmd.val, err = rd.ReadFloat()
2020-09-17 12:27:16 +03:00
return err
2013-09-29 12:06:49 +04:00
}
//------------------------------------------------------------------------------
2021-03-05 21:02:53 +03:00
type FloatSliceCmd struct {
baseCmd
val []float64
}
var _ Cmder = (*FloatSliceCmd)(nil)
func NewFloatSliceCmd(ctx context.Context, args ...interface{}) *FloatSliceCmd {
return &FloatSliceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *FloatSliceCmd) SetVal(val []float64) {
cmd.val = val
}
2021-03-05 21:02:53 +03:00
func (cmd *FloatSliceCmd) Val() []float64 {
return cmd.val
}
func (cmd *FloatSliceCmd) Result() ([]float64, error) {
return cmd.val, cmd.err
}
func (cmd *FloatSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *FloatSliceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]float64, n)
for i := 0; i < len(cmd.val); i++ {
switch num, err := rd.ReadFloat(); {
case err == Nil:
cmd.val[i] = 0
case err != nil:
return err
default:
cmd.val[i] = num
2021-03-05 21:02:53 +03:00
}
}
return nil
2021-03-05 21:02:53 +03:00
}
//------------------------------------------------------------------------------
2013-09-29 12:06:49 +04:00
type StringSliceCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val []string
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*StringSliceCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewStringSliceCmd(ctx context.Context, args ...interface{}) *StringSliceCmd {
2017-01-13 14:39:59 +03:00
return &StringSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *StringSliceCmd) SetVal(val []string) {
cmd.val = val
}
2014-01-09 12:17:38 +04:00
func (cmd *StringSliceCmd) Val() []string {
return cmd.val
2013-09-29 12:06:49 +04:00
}
func (cmd *StringSliceCmd) Result() ([]string, error) {
return cmd.Val(), cmd.Err()
}
2014-01-09 12:17:38 +04:00
func (cmd *StringSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
2017-02-01 11:36:33 +03:00
func (cmd *StringSliceCmd) ScanSlice(container interface{}) error {
return proto.ScanSlice(cmd.Val(), container)
}
2018-08-17 13:56:37 +03:00
func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]string, n)
for i := 0; i < len(cmd.val); i++ {
switch s, err := rd.ReadString(); {
case err == Nil:
cmd.val[i] = ""
case err != nil:
return err
default:
cmd.val[i] = s
}
}
return nil
}
//------------------------------------------------------------------------------
type KeyValue struct {
Key string
Value string
}
type KeyValueSliceCmd struct {
baseCmd
val []KeyValue
}
var _ Cmder = (*KeyValueSliceCmd)(nil)
func NewKeyValueSliceCmd(ctx context.Context, args ...interface{}) *KeyValueSliceCmd {
return &KeyValueSliceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
2022-08-08 07:06:04 +03:00
func (cmd *KeyValueSliceCmd) SetVal(val []KeyValue) {
cmd.val = val
}
func (cmd *KeyValueSliceCmd) Val() []KeyValue {
return cmd.val
}
func (cmd *KeyValueSliceCmd) Result() ([]KeyValue, error) {
return cmd.val, cmd.err
}
func (cmd *KeyValueSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
// Many commands will respond to two formats:
// 1. 1) "one"
// 2. (double) 1
// 2. 1) "two"
// 2. (double) 2
//
// OR:
// 1. "two"
// 2. (double) 2
// 3. "one"
// 4. (double) 1
func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
// If the n is 0, can't continue reading.
if n == 0 {
cmd.val = make([]KeyValue, 0)
return nil
}
typ, err := rd.PeekReplyType()
if err != nil {
return err
}
array := typ == proto.RespArray
if array {
cmd.val = make([]KeyValue, n)
} else {
cmd.val = make([]KeyValue, n/2)
}
for i := 0; i < len(cmd.val); i++ {
if array {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
2019-06-26 14:45:38 +03:00
}
2018-08-02 14:48:46 +03:00
}
if cmd.val[i].Key, err = rd.ReadString(); err != nil {
return err
}
if cmd.val[i].Value, err = rd.ReadString(); err != nil {
return err
}
}
return nil
2018-08-02 14:48:46 +03:00
}
2013-09-29 12:06:49 +04:00
//------------------------------------------------------------------------------
type BoolSliceCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val []bool
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*BoolSliceCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewBoolSliceCmd(ctx context.Context, args ...interface{}) *BoolSliceCmd {
2017-01-13 14:39:59 +03:00
return &BoolSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *BoolSliceCmd) SetVal(val []bool) {
cmd.val = val
}
2014-01-09 12:17:38 +04:00
func (cmd *BoolSliceCmd) Val() []bool {
return cmd.val
2013-09-29 12:06:49 +04:00
}
2014-01-09 12:17:38 +04:00
func (cmd *BoolSliceCmd) Result() ([]bool, error) {
return cmd.val, cmd.err
}
func (cmd *BoolSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]bool, n)
for i := 0; i < len(cmd.val); i++ {
if cmd.val[i], err = rd.ReadBool(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
}
return nil
2018-08-02 14:48:46 +03:00
}
2013-09-29 12:06:49 +04:00
//------------------------------------------------------------------------------
type MapStringStringCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
val map[string]string
2013-09-29 12:06:49 +04:00
}
var _ Cmder = (*MapStringStringCmd)(nil)
2017-09-26 11:29:22 +03:00
func NewMapStringStringCmd(ctx context.Context, args ...interface{}) *MapStringStringCmd {
return &MapStringStringCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *MapStringStringCmd) Val() map[string]string {
2014-01-09 12:17:38 +04:00
return cmd.val
}
chore: sync master (#2051) * Upgrade redis-server version (#1833) * Upgrade redis-server version Signed-off-by: monkey <golang@88.com> * XAutoClaim changed the return value Signed-off-by: monkey <golang@88.com> * add cmd: geosearch, geosearchstore (#1836) * add cmd: geosearch, geosearchstore Signed-off-by: monkey92t <golang@88.com> * GeoSearchQuery and GeoSearchLocationQuery changed to pointer passing Signed-off-by: monkey92t <golang@88.com> * Added missing method XInfoStreamFull to Cmdable interface * Run go mod tidy in redisotel Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com> * Revert "ConnPool check fd for bad conns (#1824)" (#1849) This reverts commit 346bfafddd36dd52d51b064033048de5552ee91e. * Automate release process (#1852) * Bump github.com/onsi/gomega from 1.10.5 to 1.14.0 (#1832) * Bump github.com/onsi/gomega from 1.10.5 to 1.14.0 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.5 to 1.14.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.10.5...v1.14.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Upgrade gomega to v1.15.0 Signed-off-by: monkey92t <golang@88.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: monkey92t <golang@88.com> * Add version.go * Fix otel example * Fix package name in release script * More fixes for otel example * And more * Fix release.sh * Release v8.11.3 (release.sh) * Create an annotated tag to give release.yml chance to run * Tweak tag.sh * Add Cmd.Slice helper to cast to []interface{} (#1859) * after the connection pool is closed, no new connections should be added (#1863) * after the connection pool is closed, no new connections should be added Signed-off-by: monkey92t <golang@88.com> * remove runGoroutine Signed-off-by: monkey92t <golang@88.com> * pool.popIdle add p.closed check Signed-off-by: monkey92t <golang@88.com> * upgrade golangci-lint v1.42.0 Signed-off-by: monkey92t <golang@88.com> * Bump github.com/onsi/gomega from 1.15.0 to 1.16.0 (#1865) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add go 1.17 to the build matrix * Remove go 1.15 from build matrix * Add scan struct example (#1870) * Replace release job * Bump github.com/cespare/xxhash/v2 from 2.1.1 to 2.1.2 (#1872) Bumps [github.com/cespare/xxhash/v2](https://github.com/cespare/xxhash) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/cespare/xxhash/releases) - [Commits](https://github.com/cespare/xxhash/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: github.com/cespare/xxhash/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix tag script to push tag by tag * Fix releasing.md * Fix/pubsub ping mutex (#1878) * Fix PubSub.Ping to hold the lock * Fix PubSub.Ping to hold the lock * add write cmd data-race test Signed-off-by: monkey92t <golang@88.com> Co-authored-by: monkey92t <golang@88.com> * chore: cleanup OpenTelemetry example * chore: gofmt all code * Refactor TestParseURL This is in preparation for supporting query parameters in ParseURL: - use an expected *Options instance to execute assertions on - extract assertions into helper function - enable parallel testing - condense test table * Add query parameter parsing to ParseURL() Before this change, ParseURL would only accept a very restricted set of URLs (it returned an error, if it encountered any parameter). This commit introduces the ability to process URLs like redis://localhost/1?dial_timeout=10s and similar. Go programs which were providing a configuration tunable (e.g. CLI flag, config entry or environment variable) to configure the Redis connection now don't need to perform this task themselves. * chore: add links to readme * chore: fix discussions link * empty hooks.withContext removed * chore: gofmt * chore: use conventional commits and auto-generate changelog * feat: add acl auth support for sentinels * chore: swap to acl auth at the test-level * Add support for BLMove command * chore: update dependencies * chore: update link * feat: add SetVal method for each command * feat: add Cmd.{String,Int,Float,Bool}Slice helpers and an example * chore: tweak GH actions to run all jobs * chore: add Lua scripting example * Fix Redis Cluster issue during roll outs of new nodes with same addr (#1914) * fix: recycle connections in some Redis Cluster scenarios This issue was surfaced in a Cloud Provider solution that used for rolling out new nodes using the same address (hostname) of the nodes that will be replaced in a Redis Cluster, while the former ones once depromoted as Slaves would continue in service during some mintues for redirecting traffic. The solution basically identifies when the connection could be stale since a MOVED response will be returned using the same address (hostname) that is being used by the connection. At that moment we consider the connection as no longer usable forcing to recycle the connection. * chore: lazy reload when moved or ask * chore: use conv commit message * chore: release v8.11.4 (release.sh) * fix: add whitespace for avoid unlikely colisions * fix: format * chore: fix links * chore: use ctx parameter in cmdInfo * Bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5 (#1925) Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.4 to 1.16.5. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v1.16.4...v1.16.5) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add support for time.Duration write and scan * test: add test case for setting and scanning durations * chore: fix linter * fix(extra/redisotel): set span.kind attribute to client According to the opentelemetry specification this should always be set to client for database client libraries. I've also removed the SetAttributes call and instead set the attributes during creation of the span. This is what the library SHOULD be doing according to the opentelemetry api specification. * chore: update otel example * fix: update some argument counts in pre-allocs In some cases number of pre-allocated places in argument array is missing 1 or 2 elements, which results in re-allocation of twice as large array * chore: add example how to delete keys without a ttl * chore: don't enable all lints * chore(deps): bump github.com/onsi/gomega from 1.16.0 to 1.17.0 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * feat: Add redis v7's NX, XX, GT, LT expire variants * chore: add missing readme * chore: tweak feature links * chore: remove Discord * fix: set timeout for WAIT command. Fixes #1963 * build: update `go` directive in `go.mod` to 1.17 This commit enables support for module graph pruning and lazy module loading for projects that are at Go 1.17 or higher. Reference: https://go.dev/ref/mod#go-mod-file-go Reference: https://go.dev/ref/mod#graph-pruning Reference: https://go.dev/ref/mod#lazy-loading Signed-off-by: Eng Zer Jun <engzerjun@gmail.com> * chore: update link * chore: export cmder.SetFirstKeyPos to support build module commands * feat(redisotel): ability to override TracerProvider (#1998) * fix: add missing Expire methods to Cmdable This is a followup to https://github.com/go-redis/redis/pull/1928 * chore(deps): bump github.com/onsi/gomega from 1.17.0 to 1.18.1 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.17.0...v1.18.1) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update README.md (#2011) chore: add fmt library in example code * chore: instrumentation name and version (#2012) * fix: invalid type assert in stringArg * chore: cleanup * fix: example/otel compile error (#2028) * fix: rename Golang to Go (#2030) https://go.dev/doc/faq#go_or_golang * feat: add support for passing extra attributes added to spans * feat: set net.peer.name and net.peer.port in otel example * chore: tweak Uptrace copy * feat: add support for COPY command (#2016) * feat: add support for acl sentinel auth in universal client * chore(deps): bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: add hll example * chore: tweak release script * chore: release v8.11.5 (release.sh) * chore: add discord back Co-authored-by: Eugene Ponizovsky <ponizovsky@gmail.com> Co-authored-by: Bogdan Drutu <bogdandrutu@gmail.com> Co-authored-by: Vladimir Mihailenco <vladimir.webdev@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kishan B <kishancs46@gmail.com> Co-authored-by: Dominik Menke <dom@digineo.de> Co-authored-by: Gökhan Özeloğlu <gozeloglu@gmail.com> Co-authored-by: Justin Sievenpiper <justin@sievenpiper.co> Co-authored-by: Алексей Романовский <aromanovsky@epiphan.com> Co-authored-by: Stavros Panakakakis <stavrospanakakis@gmail.com> Co-authored-by: Pau Freixes <pfreixes@gmail.com> Co-authored-by: Ethan Hur <ethan0311@gmail.com> Co-authored-by: Jackie <18378976+Pyrodash@users.noreply.github.com> Co-authored-by: Kristinn Björgvin Árdal <kristinnardalsecondary@gmail.com> Co-authored-by: ffenix113 <razerer@bigmir.net> Co-authored-by: Bastien Penavayre <bastienPenava@gmail.com> Co-authored-by: James3 Li(李麒傑) <james3_li@asus.com> Co-authored-by: Eng Zer Jun <engzerjun@gmail.com> Co-authored-by: gzjiangtao2014 <gzjiangtao2014@corp.netease.com> Co-authored-by: Nelz <nelz9999@users.noreply.github.com> Co-authored-by: Daniel Richter <Nexyz9@gmail.com> Co-authored-by: Seyed Ali Ghaffari <ali.ghaffari@outlook.com> Co-authored-by: lintanghui <lintanghui@bilibili.com> Co-authored-by: hidu <duv123+github@gmail.com> Co-authored-by: Jonas Lergell <jonas.lergell@volvocars.com> Co-authored-by: Alex Kahn <alexanderkahn@gmail.com>
2022-03-19 07:40:31 +03:00
func (cmd *MapStringStringCmd) SetVal(val map[string]string) {
cmd.val = val
2013-09-29 12:06:49 +04:00
}
func (cmd *MapStringStringCmd) Result() (map[string]string, error) {
2014-01-09 12:17:38 +04:00
return cmd.val, cmd.err
}
func (cmd *MapStringStringCmd) String() string {
2014-01-09 12:17:38 +04:00
return cmdString(cmd, cmd.val)
}
// Scan scans the results from the map into a destination struct. The map keys
// are matched in the Redis struct fields by the `redis:"field"` tag.
chore: sync master (#2051) * Upgrade redis-server version (#1833) * Upgrade redis-server version Signed-off-by: monkey <golang@88.com> * XAutoClaim changed the return value Signed-off-by: monkey <golang@88.com> * add cmd: geosearch, geosearchstore (#1836) * add cmd: geosearch, geosearchstore Signed-off-by: monkey92t <golang@88.com> * GeoSearchQuery and GeoSearchLocationQuery changed to pointer passing Signed-off-by: monkey92t <golang@88.com> * Added missing method XInfoStreamFull to Cmdable interface * Run go mod tidy in redisotel Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com> * Revert "ConnPool check fd for bad conns (#1824)" (#1849) This reverts commit 346bfafddd36dd52d51b064033048de5552ee91e. * Automate release process (#1852) * Bump github.com/onsi/gomega from 1.10.5 to 1.14.0 (#1832) * Bump github.com/onsi/gomega from 1.10.5 to 1.14.0 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.10.5 to 1.14.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.10.5...v1.14.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Upgrade gomega to v1.15.0 Signed-off-by: monkey92t <golang@88.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: monkey92t <golang@88.com> * Add version.go * Fix otel example * Fix package name in release script * More fixes for otel example * And more * Fix release.sh * Release v8.11.3 (release.sh) * Create an annotated tag to give release.yml chance to run * Tweak tag.sh * Add Cmd.Slice helper to cast to []interface{} (#1859) * after the connection pool is closed, no new connections should be added (#1863) * after the connection pool is closed, no new connections should be added Signed-off-by: monkey92t <golang@88.com> * remove runGoroutine Signed-off-by: monkey92t <golang@88.com> * pool.popIdle add p.closed check Signed-off-by: monkey92t <golang@88.com> * upgrade golangci-lint v1.42.0 Signed-off-by: monkey92t <golang@88.com> * Bump github.com/onsi/gomega from 1.15.0 to 1.16.0 (#1865) Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.15.0 to 1.16.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add go 1.17 to the build matrix * Remove go 1.15 from build matrix * Add scan struct example (#1870) * Replace release job * Bump github.com/cespare/xxhash/v2 from 2.1.1 to 2.1.2 (#1872) Bumps [github.com/cespare/xxhash/v2](https://github.com/cespare/xxhash) from 2.1.1 to 2.1.2. - [Release notes](https://github.com/cespare/xxhash/releases) - [Commits](https://github.com/cespare/xxhash/compare/v2.1.1...v2.1.2) --- updated-dependencies: - dependency-name: github.com/cespare/xxhash/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Fix tag script to push tag by tag * Fix releasing.md * Fix/pubsub ping mutex (#1878) * Fix PubSub.Ping to hold the lock * Fix PubSub.Ping to hold the lock * add write cmd data-race test Signed-off-by: monkey92t <golang@88.com> Co-authored-by: monkey92t <golang@88.com> * chore: cleanup OpenTelemetry example * chore: gofmt all code * Refactor TestParseURL This is in preparation for supporting query parameters in ParseURL: - use an expected *Options instance to execute assertions on - extract assertions into helper function - enable parallel testing - condense test table * Add query parameter parsing to ParseURL() Before this change, ParseURL would only accept a very restricted set of URLs (it returned an error, if it encountered any parameter). This commit introduces the ability to process URLs like redis://localhost/1?dial_timeout=10s and similar. Go programs which were providing a configuration tunable (e.g. CLI flag, config entry or environment variable) to configure the Redis connection now don't need to perform this task themselves. * chore: add links to readme * chore: fix discussions link * empty hooks.withContext removed * chore: gofmt * chore: use conventional commits and auto-generate changelog * feat: add acl auth support for sentinels * chore: swap to acl auth at the test-level * Add support for BLMove command * chore: update dependencies * chore: update link * feat: add SetVal method for each command * feat: add Cmd.{String,Int,Float,Bool}Slice helpers and an example * chore: tweak GH actions to run all jobs * chore: add Lua scripting example * Fix Redis Cluster issue during roll outs of new nodes with same addr (#1914) * fix: recycle connections in some Redis Cluster scenarios This issue was surfaced in a Cloud Provider solution that used for rolling out new nodes using the same address (hostname) of the nodes that will be replaced in a Redis Cluster, while the former ones once depromoted as Slaves would continue in service during some mintues for redirecting traffic. The solution basically identifies when the connection could be stale since a MOVED response will be returned using the same address (hostname) that is being used by the connection. At that moment we consider the connection as no longer usable forcing to recycle the connection. * chore: lazy reload when moved or ask * chore: use conv commit message * chore: release v8.11.4 (release.sh) * fix: add whitespace for avoid unlikely colisions * fix: format * chore: fix links * chore: use ctx parameter in cmdInfo * Bump github.com/onsi/ginkgo from 1.16.4 to 1.16.5 (#1925) Bumps [github.com/onsi/ginkgo](https://github.com/onsi/ginkgo) from 1.16.4 to 1.16.5. - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v1.16.4...v1.16.5) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: add support for time.Duration write and scan * test: add test case for setting and scanning durations * chore: fix linter * fix(extra/redisotel): set span.kind attribute to client According to the opentelemetry specification this should always be set to client for database client libraries. I've also removed the SetAttributes call and instead set the attributes during creation of the span. This is what the library SHOULD be doing according to the opentelemetry api specification. * chore: update otel example * fix: update some argument counts in pre-allocs In some cases number of pre-allocated places in argument array is missing 1 or 2 elements, which results in re-allocation of twice as large array * chore: add example how to delete keys without a ttl * chore: don't enable all lints * chore(deps): bump github.com/onsi/gomega from 1.16.0 to 1.17.0 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.16.0 to 1.17.0. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.16.0...v1.17.0) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * feat: Add redis v7's NX, XX, GT, LT expire variants * chore: add missing readme * chore: tweak feature links * chore: remove Discord * fix: set timeout for WAIT command. Fixes #1963 * build: update `go` directive in `go.mod` to 1.17 This commit enables support for module graph pruning and lazy module loading for projects that are at Go 1.17 or higher. Reference: https://go.dev/ref/mod#go-mod-file-go Reference: https://go.dev/ref/mod#graph-pruning Reference: https://go.dev/ref/mod#lazy-loading Signed-off-by: Eng Zer Jun <engzerjun@gmail.com> * chore: update link * chore: export cmder.SetFirstKeyPos to support build module commands * feat(redisotel): ability to override TracerProvider (#1998) * fix: add missing Expire methods to Cmdable This is a followup to https://github.com/go-redis/redis/pull/1928 * chore(deps): bump github.com/onsi/gomega from 1.17.0 to 1.18.1 Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.17.0 to 1.18.1. - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.17.0...v1.18.1) --- updated-dependencies: - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Update README.md (#2011) chore: add fmt library in example code * chore: instrumentation name and version (#2012) * fix: invalid type assert in stringArg * chore: cleanup * fix: example/otel compile error (#2028) * fix: rename Golang to Go (#2030) https://go.dev/doc/faq#go_or_golang * feat: add support for passing extra attributes added to spans * feat: set net.peer.name and net.peer.port in otel example * chore: tweak Uptrace copy * feat: add support for COPY command (#2016) * feat: add support for acl sentinel auth in universal client * chore(deps): bump actions/checkout from 2 to 3 Bumps [actions/checkout](https://github.com/actions/checkout) from 2 to 3. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * chore: add hll example * chore: tweak release script * chore: release v8.11.5 (release.sh) * chore: add discord back Co-authored-by: Eugene Ponizovsky <ponizovsky@gmail.com> Co-authored-by: Bogdan Drutu <bogdandrutu@gmail.com> Co-authored-by: Vladimir Mihailenco <vladimir.webdev@gmail.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Kishan B <kishancs46@gmail.com> Co-authored-by: Dominik Menke <dom@digineo.de> Co-authored-by: Gökhan Özeloğlu <gozeloglu@gmail.com> Co-authored-by: Justin Sievenpiper <justin@sievenpiper.co> Co-authored-by: Алексей Романовский <aromanovsky@epiphan.com> Co-authored-by: Stavros Panakakakis <stavrospanakakis@gmail.com> Co-authored-by: Pau Freixes <pfreixes@gmail.com> Co-authored-by: Ethan Hur <ethan0311@gmail.com> Co-authored-by: Jackie <18378976+Pyrodash@users.noreply.github.com> Co-authored-by: Kristinn Björgvin Árdal <kristinnardalsecondary@gmail.com> Co-authored-by: ffenix113 <razerer@bigmir.net> Co-authored-by: Bastien Penavayre <bastienPenava@gmail.com> Co-authored-by: James3 Li(李麒傑) <james3_li@asus.com> Co-authored-by: Eng Zer Jun <engzerjun@gmail.com> Co-authored-by: gzjiangtao2014 <gzjiangtao2014@corp.netease.com> Co-authored-by: Nelz <nelz9999@users.noreply.github.com> Co-authored-by: Daniel Richter <Nexyz9@gmail.com> Co-authored-by: Seyed Ali Ghaffari <ali.ghaffari@outlook.com> Co-authored-by: lintanghui <lintanghui@bilibili.com> Co-authored-by: hidu <duv123+github@gmail.com> Co-authored-by: Jonas Lergell <jonas.lergell@volvocars.com> Co-authored-by: Alex Kahn <alexanderkahn@gmail.com>
2022-03-19 07:40:31 +03:00
func (cmd *MapStringStringCmd) Scan(dest interface{}) error {
if cmd.err != nil {
return cmd.err
}
2021-08-29 19:37:17 +03:00
strct, err := hscan.Struct(dest)
if err != nil {
return err
}
for k, v := range cmd.val {
if err := strct.Scan(k, v); err != nil {
return err
}
}
return nil
}
func (cmd *MapStringStringCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
2013-09-29 12:06:49 +04:00
cmd.val = make(map[string]string, n)
for i := 0; i < n; i++ {
key, err := rd.ReadString()
if err != nil {
return err
}
2018-08-02 14:48:46 +03:00
value, err := rd.ReadString()
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
cmd.val[key] = value
}
return nil
2018-08-02 14:48:46 +03:00
}
2013-09-29 12:06:49 +04:00
//------------------------------------------------------------------------------
2022-08-08 10:28:46 +03:00
type MapStringIntCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2015-01-25 15:05:19 +03:00
val map[string]int64
}
2022-08-08 10:28:46 +03:00
var _ Cmder = (*MapStringIntCmd)(nil)
2017-09-26 11:29:22 +03:00
2022-08-08 10:28:46 +03:00
func NewMapStringIntCmd(ctx context.Context, args ...interface{}) *MapStringIntCmd {
return &MapStringIntCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2015-01-25 15:05:19 +03:00
}
2022-08-08 10:28:46 +03:00
func (cmd *MapStringIntCmd) SetVal(val map[string]int64) {
cmd.val = val
}
2022-08-08 10:28:46 +03:00
func (cmd *MapStringIntCmd) Val() map[string]int64 {
2015-01-25 15:05:19 +03:00
return cmd.val
}
2022-08-08 10:28:46 +03:00
func (cmd *MapStringIntCmd) Result() (map[string]int64, error) {
2015-01-25 15:05:19 +03:00
return cmd.val, cmd.err
}
2022-08-08 10:28:46 +03:00
func (cmd *MapStringIntCmd) String() string {
2015-01-25 15:05:19 +03:00
return cmdString(cmd, cmd.val)
}
2022-08-08 10:28:46 +03:00
func (cmd *MapStringIntCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
2015-01-25 15:05:19 +03:00
cmd.val = make(map[string]int64, n)
for i := 0; i < n; i++ {
key, err := rd.ReadString()
if err != nil {
return err
}
2018-08-02 14:48:46 +03:00
nn, err := rd.ReadInt()
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
cmd.val[key] = nn
}
return nil
2018-08-02 14:48:46 +03:00
}
2015-01-25 15:05:19 +03:00
//------------------------------------------------------------------------------
2017-11-19 19:36:23 +03:00
type StringStructMapCmd struct {
baseCmd
val map[string]struct{}
}
var _ Cmder = (*StringStructMapCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewStringStructMapCmd(ctx context.Context, args ...interface{}) *StringStructMapCmd {
2017-11-19 19:36:23 +03:00
return &StringStructMapCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-11-19 19:36:23 +03:00
}
}
func (cmd *StringStructMapCmd) SetVal(val map[string]struct{}) {
cmd.val = val
}
2017-11-19 19:36:23 +03:00
func (cmd *StringStructMapCmd) Val() map[string]struct{} {
return cmd.val
}
func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) {
return cmd.val, cmd.err
}
func (cmd *StringStructMapCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make(map[string]struct{}, n)
for i := 0; i < n; i++ {
key, err := rd.ReadString()
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
cmd.val[key] = struct{}{}
}
return nil
2017-11-25 05:06:13 +03:00
}
2018-08-02 14:48:46 +03:00
//------------------------------------------------------------------------------
2017-11-25 05:06:13 +03:00
type XMessage struct {
ID string
Values map[string]interface{}
}
2018-08-02 14:48:46 +03:00
type XMessageSliceCmd struct {
2017-11-25 05:06:13 +03:00
baseCmd
2018-08-02 14:48:46 +03:00
val []XMessage
2017-11-25 05:06:13 +03:00
}
2018-08-02 14:48:46 +03:00
var _ Cmder = (*XMessageSliceCmd)(nil)
2017-11-25 05:06:13 +03:00
2020-03-11 17:26:42 +03:00
func NewXMessageSliceCmd(ctx context.Context, args ...interface{}) *XMessageSliceCmd {
2018-08-02 14:48:46 +03:00
return &XMessageSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-11-25 05:06:13 +03:00
}
}
func (cmd *XMessageSliceCmd) SetVal(val []XMessage) {
cmd.val = val
}
2018-08-02 14:48:46 +03:00
func (cmd *XMessageSliceCmd) Val() []XMessage {
2017-11-25 05:06:13 +03:00
return cmd.val
}
2018-08-02 14:48:46 +03:00
func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) {
2017-11-25 05:06:13 +03:00
return cmd.val, cmd.err
}
2018-08-02 14:48:46 +03:00
func (cmd *XMessageSliceCmd) String() string {
2017-11-25 05:06:13 +03:00
return cmdString(cmd, cmd.val)
}
func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) (err error) {
2020-09-24 09:06:17 +03:00
cmd.val, err = readXMessageSlice(rd)
return err
}
func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) {
n, err := rd.ReadArrayLen()
2020-09-17 12:27:16 +03:00
if err != nil {
2020-09-24 09:06:17 +03:00
return nil, err
2017-11-25 05:06:13 +03:00
}
2020-09-24 09:06:17 +03:00
msgs := make([]XMessage, n)
for i := 0; i < len(msgs); i++ {
if msgs[i], err = readXMessage(rd); err != nil {
2020-09-24 09:06:17 +03:00
return nil, err
}
}
return msgs, nil
2017-11-25 05:06:13 +03:00
}
2020-09-22 20:45:23 +03:00
func readXMessage(rd *proto.Reader) (XMessage, error) {
if err := rd.ReadFixedArrayLen(2); err != nil {
2020-09-22 20:45:23 +03:00
return XMessage{}, err
}
2018-08-02 14:48:46 +03:00
2020-09-22 20:45:23 +03:00
id, err := rd.ReadString()
if err != nil {
return XMessage{}, err
}
2018-08-02 14:48:46 +03:00
v, err := stringInterfaceMapParser(rd)
2020-09-22 20:45:23 +03:00
if err != nil {
if err != proto.Nil {
return XMessage{}, err
}
}
return XMessage{
ID: id,
Values: v,
2020-09-22 20:45:23 +03:00
}, nil
}
func stringInterfaceMapParser(rd *proto.Reader) (map[string]interface{}, error) {
n, err := rd.ReadMapLen()
if err != nil {
return nil, err
}
m := make(map[string]interface{}, n)
for i := 0; i < n; i++ {
2018-08-16 13:25:19 +03:00
key, err := rd.ReadString()
2018-08-02 14:48:46 +03:00
if err != nil {
return nil, err
}
2017-11-25 05:06:13 +03:00
2018-08-16 13:25:19 +03:00
value, err := rd.ReadString()
2018-08-02 14:48:46 +03:00
if err != nil {
return nil, err
}
2017-11-25 05:06:13 +03:00
2018-08-02 14:48:46 +03:00
m[key] = value
2017-11-25 05:06:13 +03:00
}
2018-08-02 14:48:46 +03:00
return m, nil
2017-11-25 05:06:13 +03:00
}
//------------------------------------------------------------------------------
2018-08-02 14:48:46 +03:00
type XStream struct {
Stream string
Messages []XMessage
}
type XStreamSliceCmd struct {
2017-11-25 05:06:13 +03:00
baseCmd
2018-08-02 14:48:46 +03:00
val []XStream
2017-11-25 05:06:13 +03:00
}
2018-08-02 14:48:46 +03:00
var _ Cmder = (*XStreamSliceCmd)(nil)
2017-11-25 05:06:13 +03:00
2020-03-11 17:26:42 +03:00
func NewXStreamSliceCmd(ctx context.Context, args ...interface{}) *XStreamSliceCmd {
2018-08-02 14:48:46 +03:00
return &XStreamSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-11-25 05:06:13 +03:00
}
}
func (cmd *XStreamSliceCmd) SetVal(val []XStream) {
cmd.val = val
}
2018-08-02 14:48:46 +03:00
func (cmd *XStreamSliceCmd) Val() []XStream {
2017-11-25 05:06:13 +03:00
return cmd.val
}
2018-08-02 14:48:46 +03:00
func (cmd *XStreamSliceCmd) Result() ([]XStream, error) {
2017-11-25 05:06:13 +03:00
return cmd.val, cmd.err
}
2018-08-02 14:48:46 +03:00
func (cmd *XStreamSliceCmd) String() string {
2017-11-25 05:06:13 +03:00
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error {
typ, err := rd.PeekReplyType()
if err != nil {
return err
}
2018-08-02 14:48:46 +03:00
var n int
if typ == proto.RespMap {
n, err = rd.ReadMapLen()
} else {
n, err = rd.ReadArrayLen()
}
if err != nil {
return err
}
cmd.val = make([]XStream, n)
for i := 0; i < len(cmd.val); i++ {
if typ != proto.RespMap {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
2017-11-25 05:06:13 +03:00
}
if cmd.val[i].Stream, err = rd.ReadString(); err != nil {
return err
}
if cmd.val[i].Messages, err = readXMessageSlice(rd); err != nil {
return err
}
}
return nil
2017-11-25 05:06:13 +03:00
}
2018-08-02 14:48:46 +03:00
//------------------------------------------------------------------------------
type XPending struct {
Count int64
Lower string
Higher string
Consumers map[string]int64
}
type XPendingCmd struct {
baseCmd
val *XPending
}
var _ Cmder = (*XPendingCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewXPendingCmd(ctx context.Context, args ...interface{}) *XPendingCmd {
2018-08-02 14:48:46 +03:00
return &XPendingCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2018-08-02 14:48:46 +03:00
}
}
func (cmd *XPendingCmd) SetVal(val *XPending) {
cmd.val = val
}
2018-08-02 14:48:46 +03:00
func (cmd *XPendingCmd) Val() *XPending {
return cmd.val
}
func (cmd *XPendingCmd) Result() (*XPending, error) {
return cmd.val, cmd.err
}
func (cmd *XPendingCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *XPendingCmd) readReply(rd *proto.Reader) error {
var err error
if err = rd.ReadFixedArrayLen(4); err != nil {
return err
}
cmd.val = &XPending{}
2017-11-25 05:06:13 +03:00
if cmd.val.Count, err = rd.ReadInt(); err != nil {
return err
}
2017-11-25 05:06:13 +03:00
if cmd.val.Lower, err = rd.ReadString(); err != nil && err != Nil {
return err
}
2018-08-02 14:48:46 +03:00
if cmd.val.Higher, err = rd.ReadString(); err != nil && err != Nil {
return err
}
2018-08-02 14:48:46 +03:00
n, err := rd.ReadArrayLen()
if err != nil && err != Nil {
return err
}
cmd.val.Consumers = make(map[string]int64, n)
for i := 0; i < n; i++ {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
2019-06-26 14:45:38 +03:00
}
consumerName, err := rd.ReadString()
if err != nil {
return err
2017-11-25 05:06:13 +03:00
}
consumerPending, err := rd.ReadInt()
if err != nil {
return err
}
cmd.val.Consumers[consumerName] = consumerPending
}
return nil
2018-08-02 14:48:46 +03:00
}
//------------------------------------------------------------------------------
type XPendingExt struct {
2019-05-16 16:27:19 +03:00
ID string
2018-08-02 14:48:46 +03:00
Consumer string
Idle time.Duration
RetryCount int64
}
type XPendingExtCmd struct {
baseCmd
val []XPendingExt
}
var _ Cmder = (*XPendingExtCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewXPendingExtCmd(ctx context.Context, args ...interface{}) *XPendingExtCmd {
2018-08-02 14:48:46 +03:00
return &XPendingExtCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2018-08-02 14:48:46 +03:00
}
}
func (cmd *XPendingExtCmd) SetVal(val []XPendingExt) {
cmd.val = val
}
2018-08-02 14:48:46 +03:00
func (cmd *XPendingExtCmd) Val() []XPendingExt {
return cmd.val
}
func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) {
return cmd.val, cmd.err
}
func (cmd *XPendingExtCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]XPendingExt, n)
2018-08-02 14:48:46 +03:00
for i := 0; i < len(cmd.val); i++ {
if err = rd.ReadFixedArrayLen(4); err != nil {
return err
}
2018-08-02 14:48:46 +03:00
if cmd.val[i].ID, err = rd.ReadString(); err != nil {
return err
}
2018-08-02 14:48:46 +03:00
if cmd.val[i].Consumer, err = rd.ReadString(); err != nil && err != Nil {
return err
}
2018-08-02 14:48:46 +03:00
idle, err := rd.ReadInt()
if err != nil && err != Nil {
return err
}
cmd.val[i].Idle = time.Duration(idle) * time.Millisecond
2018-08-02 14:48:46 +03:00
if cmd.val[i].RetryCount, err = rd.ReadInt(); err != nil && err != Nil {
return err
2017-11-25 05:06:13 +03:00
}
}
return nil
2017-11-25 05:06:13 +03:00
}
//------------------------------------------------------------------------------
2021-06-04 18:04:36 +03:00
type XAutoClaimCmd struct {
baseCmd
start string
val []XMessage
}
var _ Cmder = (*XAutoClaimCmd)(nil)
func NewXAutoClaimCmd(ctx context.Context, args ...interface{}) *XAutoClaimCmd {
return &XAutoClaimCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *XAutoClaimCmd) SetVal(val []XMessage, start string) {
cmd.val = val
cmd.start = start
}
2021-06-04 18:04:36 +03:00
func (cmd *XAutoClaimCmd) Val() (messages []XMessage, start string) {
return cmd.val, cmd.start
}
func (cmd *XAutoClaimCmd) Result() (messages []XMessage, start string, err error) {
return cmd.val, cmd.start, cmd.err
}
func (cmd *XAutoClaimCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XAutoClaimCmd) readReply(rd *proto.Reader) error {
2022-06-04 17:25:12 +03:00
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
2021-06-04 18:04:36 +03:00
2022-06-04 17:25:12 +03:00
switch n {
case 2, // Redis 6
3: // Redis 7:
// ok
default:
return fmt.Errorf("redis: got %d elements in XAutoClaim reply, wanted 2/3", n)
}
cmd.start, err = rd.ReadString()
if err != nil {
return err
}
2022-06-04 17:25:12 +03:00
cmd.val, err = readXMessageSlice(rd)
if err != nil {
return err
}
2022-06-04 17:25:12 +03:00
if n >= 3 {
if err := rd.DiscardNext(); err != nil {
return err
}
}
return nil
2021-06-04 18:04:36 +03:00
}
//------------------------------------------------------------------------------
type XAutoClaimJustIDCmd struct {
baseCmd
start string
val []string
}
var _ Cmder = (*XAutoClaimJustIDCmd)(nil)
func NewXAutoClaimJustIDCmd(ctx context.Context, args ...interface{}) *XAutoClaimJustIDCmd {
return &XAutoClaimJustIDCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *XAutoClaimJustIDCmd) SetVal(val []string, start string) {
cmd.val = val
cmd.start = start
}
2021-06-04 18:04:36 +03:00
func (cmd *XAutoClaimJustIDCmd) Val() (ids []string, start string) {
return cmd.val, cmd.start
}
func (cmd *XAutoClaimJustIDCmd) Result() (ids []string, start string, err error) {
return cmd.val, cmd.start, cmd.err
}
func (cmd *XAutoClaimJustIDCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XAutoClaimJustIDCmd) readReply(rd *proto.Reader) error {
2022-06-04 17:25:12 +03:00
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
2021-06-04 18:04:36 +03:00
2022-06-04 17:25:12 +03:00
switch n {
case 2, // Redis 6
3: // Redis 7:
// ok
default:
return fmt.Errorf("redis: got %d elements in XAutoClaimJustID reply, wanted 2/3", n)
}
cmd.start, err = rd.ReadString()
if err != nil {
return err
}
2022-06-04 17:25:12 +03:00
nn, err := rd.ReadArrayLen()
if err != nil {
return err
}
2021-06-04 18:04:36 +03:00
2022-06-04 17:25:12 +03:00
cmd.val = make([]string, nn)
for i := 0; i < nn; i++ {
cmd.val[i], err = rd.ReadString()
2021-06-04 18:04:36 +03:00
if err != nil {
return err
2021-06-04 18:04:36 +03:00
}
}
2022-06-04 17:25:12 +03:00
if n >= 3 {
if err := rd.DiscardNext(); err != nil {
return err
}
}
return nil
2021-06-04 18:04:36 +03:00
}
//------------------------------------------------------------------------------
type XInfoConsumersCmd struct {
baseCmd
val []XInfoConsumer
}
type XInfoConsumer struct {
Name string
Pending int64
Idle time.Duration
}
var _ Cmder = (*XInfoConsumersCmd)(nil)
func NewXInfoConsumersCmd(ctx context.Context, stream string, group string) *XInfoConsumersCmd {
return &XInfoConsumersCmd{
baseCmd: baseCmd{
ctx: ctx,
args: []interface{}{"xinfo", "consumers", stream, group},
},
}
}
func (cmd *XInfoConsumersCmd) SetVal(val []XInfoConsumer) {
cmd.val = val
}
func (cmd *XInfoConsumersCmd) Val() []XInfoConsumer {
return cmd.val
}
func (cmd *XInfoConsumersCmd) Result() ([]XInfoConsumer, error) {
return cmd.val, cmd.err
}
func (cmd *XInfoConsumersCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XInfoConsumersCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]XInfoConsumer, n)
for i := 0; i < len(cmd.val); i++ {
if err = rd.ReadFixedMapLen(3); err != nil {
return err
}
var key string
for f := 0; f < 3; f++ {
key, err = rd.ReadString()
if err != nil {
return err
}
switch key {
case "name":
cmd.val[i].Name, err = rd.ReadString()
case "pending":
cmd.val[i].Pending, err = rd.ReadInt()
case "idle":
var idle int64
idle, err = rd.ReadInt()
cmd.val[i].Idle = time.Duration(idle) * time.Millisecond
default:
return fmt.Errorf("redis: unexpected content %s in XINFO CONSUMERS reply", key)
}
if err != nil {
return err
}
}
}
return nil
}
//------------------------------------------------------------------------------
type XInfoGroupsCmd struct {
baseCmd
2020-09-23 11:00:34 +03:00
val []XInfoGroup
}
2020-09-23 11:00:34 +03:00
type XInfoGroup struct {
Name string
Consumers int64
Pending int64
LastDeliveredID string
2022-06-04 17:25:12 +03:00
EntriesRead int64
Lag int64
}
var _ Cmder = (*XInfoGroupsCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd {
return &XInfoGroupsCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: []interface{}{"xinfo", "groups", stream},
},
}
}
func (cmd *XInfoGroupsCmd) SetVal(val []XInfoGroup) {
cmd.val = val
}
2020-09-23 11:00:34 +03:00
func (cmd *XInfoGroupsCmd) Val() []XInfoGroup {
return cmd.val
}
2020-09-23 11:00:34 +03:00
func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) {
return cmd.val, cmd.err
}
func (cmd *XInfoGroupsCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error {
2020-09-23 11:00:34 +03:00
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]XInfoGroup, n)
for i := 0; i < len(cmd.val); i++ {
2022-06-04 17:25:12 +03:00
group := &cmd.val[i]
nn, err := rd.ReadMapLen()
if err != nil {
2020-09-23 11:00:34 +03:00
return err
2020-09-17 12:27:16 +03:00
}
2020-09-23 11:00:34 +03:00
var key string
2022-06-04 17:25:12 +03:00
for j := 0; j < nn; j++ {
key, err = rd.ReadString()
2020-09-23 11:00:34 +03:00
if err != nil {
return err
}
switch key {
case "name":
2022-06-04 17:25:12 +03:00
group.Name, err = rd.ReadString()
if err != nil {
return err
}
case "consumers":
2022-06-04 17:25:12 +03:00
group.Consumers, err = rd.ReadInt()
if err != nil {
return err
}
case "pending":
2022-06-04 17:25:12 +03:00
group.Pending, err = rd.ReadInt()
if err != nil {
return err
}
case "last-delivered-id":
2022-06-04 17:25:12 +03:00
group.LastDeliveredID, err = rd.ReadString()
if err != nil {
return err
}
case "entries-read":
group.EntriesRead, err = rd.ReadInt()
if err != nil && err != Nil {
2022-06-04 17:25:12 +03:00
return err
}
case "lag":
group.Lag, err = rd.ReadInt()
if err != nil {
return err
}
default:
2022-06-04 17:25:12 +03:00
return fmt.Errorf("redis: unexpected key %q in XINFO GROUPS reply", key)
2020-09-23 11:00:34 +03:00
}
}
}
2020-09-23 11:00:34 +03:00
return nil
}
//------------------------------------------------------------------------------
2020-09-22 20:45:23 +03:00
type XInfoStreamCmd struct {
baseCmd
val *XInfoStream
}
type XInfoStream struct {
2022-06-04 17:25:12 +03:00
Length int64
RadixTreeKeys int64
RadixTreeNodes int64
Groups int64
LastGeneratedID string
MaxDeletedEntryID string
EntriesAdded int64
FirstEntry XMessage
LastEntry XMessage
RecordedFirstEntryID string
2020-09-22 20:45:23 +03:00
}
var _ Cmder = (*XInfoStreamCmd)(nil)
func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd {
return &XInfoStreamCmd{
baseCmd: baseCmd{
ctx: ctx,
args: []interface{}{"xinfo", "stream", stream},
},
}
}
func (cmd *XInfoStreamCmd) SetVal(val *XInfoStream) {
cmd.val = val
}
2020-09-22 20:45:23 +03:00
func (cmd *XInfoStreamCmd) Val() *XInfoStream {
return cmd.val
}
func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) {
return cmd.val, cmd.err
}
func (cmd *XInfoStreamCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error {
2022-06-04 17:25:12 +03:00
n, err := rd.ReadMapLen()
if err != nil {
2020-09-22 20:45:23 +03:00
return err
}
cmd.val = &XInfoStream{}
2020-09-22 20:45:23 +03:00
2022-06-04 17:25:12 +03:00
for i := 0; i < n; i++ {
2020-09-22 20:45:23 +03:00
key, err := rd.ReadString()
if err != nil {
return err
2020-09-22 20:45:23 +03:00
}
switch key {
case "length":
cmd.val.Length, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
2020-09-22 20:45:23 +03:00
case "radix-tree-keys":
cmd.val.RadixTreeKeys, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
2020-09-22 20:45:23 +03:00
case "radix-tree-nodes":
cmd.val.RadixTreeNodes, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
2020-09-22 20:45:23 +03:00
case "groups":
cmd.val.Groups, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
2020-09-22 20:45:23 +03:00
case "last-generated-id":
cmd.val.LastGeneratedID, err = rd.ReadString()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "max-deleted-entry-id":
cmd.val.MaxDeletedEntryID, err = rd.ReadString()
if err != nil {
return err
}
case "entries-added":
cmd.val.EntriesAdded, err = rd.ReadInt()
if err != nil {
return err
}
2020-09-22 20:45:23 +03:00
case "first-entry":
cmd.val.FirstEntry, err = readXMessage(rd)
2022-06-04 17:25:12 +03:00
if err != nil && err != Nil {
return err
2021-05-19 11:52:13 +03:00
}
2020-09-22 20:45:23 +03:00
case "last-entry":
cmd.val.LastEntry, err = readXMessage(rd)
2022-06-04 17:25:12 +03:00
if err != nil && err != Nil {
return err
}
case "recorded-first-entry-id":
cmd.val.RecordedFirstEntryID, err = rd.ReadString()
if err != nil {
return err
2021-05-19 11:52:13 +03:00
}
2020-09-22 20:45:23 +03:00
default:
2022-06-04 17:25:12 +03:00
return fmt.Errorf("redis: unexpected key %q in XINFO STREAM reply", key)
2020-09-22 20:45:23 +03:00
}
}
return nil
2020-09-22 20:45:23 +03:00
}
//------------------------------------------------------------------------------
type XInfoStreamFullCmd struct {
baseCmd
val *XInfoStreamFull
}
type XInfoStreamFull struct {
2022-06-04 17:25:12 +03:00
Length int64
RadixTreeKeys int64
RadixTreeNodes int64
LastGeneratedID string
MaxDeletedEntryID string
EntriesAdded int64
Entries []XMessage
Groups []XInfoStreamGroup
RecordedFirstEntryID string
}
type XInfoStreamGroup struct {
Name string
LastDeliveredID string
2022-06-04 17:25:12 +03:00
EntriesRead int64
Lag int64
PelCount int64
Pending []XInfoStreamGroupPending
Consumers []XInfoStreamConsumer
}
type XInfoStreamGroupPending struct {
ID string
Consumer string
DeliveryTime time.Time
DeliveryCount int64
}
type XInfoStreamConsumer struct {
Name string
SeenTime time.Time
PelCount int64
Pending []XInfoStreamConsumerPending
}
type XInfoStreamConsumerPending struct {
ID string
DeliveryTime time.Time
DeliveryCount int64
}
var _ Cmder = (*XInfoStreamFullCmd)(nil)
func NewXInfoStreamFullCmd(ctx context.Context, args ...interface{}) *XInfoStreamFullCmd {
return &XInfoStreamFullCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *XInfoStreamFullCmd) SetVal(val *XInfoStreamFull) {
cmd.val = val
}
func (cmd *XInfoStreamFullCmd) Val() *XInfoStreamFull {
return cmd.val
}
func (cmd *XInfoStreamFullCmd) Result() (*XInfoStreamFull, error) {
return cmd.val, cmd.err
}
func (cmd *XInfoStreamFullCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *XInfoStreamFullCmd) readReply(rd *proto.Reader) error {
2022-06-04 17:25:12 +03:00
n, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val = &XInfoStreamFull{}
2022-06-04 17:25:12 +03:00
for i := 0; i < n; i++ {
key, err := rd.ReadString()
if err != nil {
return err
}
switch key {
case "length":
cmd.val.Length, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "radix-tree-keys":
cmd.val.RadixTreeKeys, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "radix-tree-nodes":
cmd.val.RadixTreeNodes, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "last-generated-id":
cmd.val.LastGeneratedID, err = rd.ReadString()
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "entries-added":
cmd.val.EntriesAdded, err = rd.ReadInt()
if err != nil {
return err
}
case "entries":
cmd.val.Entries, err = readXMessageSlice(rd)
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "groups":
cmd.val.Groups, err = readStreamGroups(rd)
2022-06-04 17:25:12 +03:00
if err != nil {
return err
}
case "max-deleted-entry-id":
cmd.val.MaxDeletedEntryID, err = rd.ReadString()
if err != nil {
return err
}
case "recorded-first-entry-id":
cmd.val.RecordedFirstEntryID, err = rd.ReadString()
if err != nil {
return err
}
default:
2022-06-04 17:25:12 +03:00
return fmt.Errorf("redis: unexpected key %q in XINFO STREAM FULL reply", key)
}
}
return nil
}
func readStreamGroups(rd *proto.Reader) ([]XInfoStreamGroup, error) {
n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
groups := make([]XInfoStreamGroup, 0, n)
for i := 0; i < n; i++ {
2022-06-04 17:25:12 +03:00
nn, err := rd.ReadMapLen()
if err != nil {
return nil, err
}
group := XInfoStreamGroup{}
2022-06-04 17:25:12 +03:00
for j := 0; j < nn; j++ {
key, err := rd.ReadString()
if err != nil {
return nil, err
}
switch key {
case "name":
group.Name, err = rd.ReadString()
2022-06-04 17:25:12 +03:00
if err != nil {
return nil, err
}
case "last-delivered-id":
group.LastDeliveredID, err = rd.ReadString()
2022-06-04 17:25:12 +03:00
if err != nil {
return nil, err
}
case "entries-read":
group.EntriesRead, err = rd.ReadInt()
if err != nil {
return nil, err
}
case "lag":
group.Lag, err = rd.ReadInt()
if err != nil {
return nil, err
}
case "pel-count":
group.PelCount, err = rd.ReadInt()
2022-06-04 17:25:12 +03:00
if err != nil {
return nil, err
}
case "pending":
group.Pending, err = readXInfoStreamGroupPending(rd)
2022-06-04 17:25:12 +03:00
if err != nil {
return nil, err
}
case "consumers":
group.Consumers, err = readXInfoStreamConsumers(rd)
2022-06-04 17:25:12 +03:00
if err != nil {
return nil, err
}
default:
2022-06-04 17:25:12 +03:00
return nil, fmt.Errorf("redis: unexpected key %q in XINFO STREAM FULL reply", key)
}
}
groups = append(groups, group)
}
return groups, nil
}
func readXInfoStreamGroupPending(rd *proto.Reader) ([]XInfoStreamGroupPending, error) {
n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
pending := make([]XInfoStreamGroupPending, 0, n)
for i := 0; i < n; i++ {
if err = rd.ReadFixedArrayLen(4); err != nil {
return nil, err
}
p := XInfoStreamGroupPending{}
p.ID, err = rd.ReadString()
if err != nil {
return nil, err
}
p.Consumer, err = rd.ReadString()
if err != nil {
return nil, err
}
delivery, err := rd.ReadInt()
if err != nil {
return nil, err
}
p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
p.DeliveryCount, err = rd.ReadInt()
if err != nil {
return nil, err
}
pending = append(pending, p)
}
return pending, nil
}
func readXInfoStreamConsumers(rd *proto.Reader) ([]XInfoStreamConsumer, error) {
n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
consumers := make([]XInfoStreamConsumer, 0, n)
for i := 0; i < n; i++ {
if err = rd.ReadFixedMapLen(4); err != nil {
return nil, err
}
c := XInfoStreamConsumer{}
for f := 0; f < 4; f++ {
cKey, err := rd.ReadString()
if err != nil {
return nil, err
}
switch cKey {
case "name":
c.Name, err = rd.ReadString()
case "seen-time":
seen, err := rd.ReadInt()
if err != nil {
return nil, err
}
c.SeenTime = time.Unix(seen/1000, seen%1000*int64(time.Millisecond))
case "pel-count":
c.PelCount, err = rd.ReadInt()
case "pending":
pendingNumber, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}
c.Pending = make([]XInfoStreamConsumerPending, 0, pendingNumber)
2021-05-19 11:52:13 +03:00
for pn := 0; pn < pendingNumber; pn++ {
if err = rd.ReadFixedArrayLen(3); err != nil {
return nil, err
}
p := XInfoStreamConsumerPending{}
p.ID, err = rd.ReadString()
if err != nil {
return nil, err
}
delivery, err := rd.ReadInt()
if err != nil {
return nil, err
}
p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond))
p.DeliveryCount, err = rd.ReadInt()
if err != nil {
return nil, err
}
c.Pending = append(c.Pending, p)
}
default:
return nil, fmt.Errorf("redis: unexpected content %s "+
"in XINFO STREAM FULL reply", cKey)
}
if err != nil {
return nil, err
}
}
consumers = append(consumers, c)
}
return consumers, nil
}
//------------------------------------------------------------------------------
2014-07-05 14:46:27 +04:00
type ZSliceCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
2014-07-05 14:46:27 +04:00
val []Z
2013-09-29 12:06:49 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*ZSliceCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd {
2017-01-13 14:39:59 +03:00
return &ZSliceCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2013-09-29 12:06:49 +04:00
}
func (cmd *ZSliceCmd) SetVal(val []Z) {
cmd.val = val
}
2014-07-05 14:46:27 +04:00
func (cmd *ZSliceCmd) Val() []Z {
2014-01-09 12:17:38 +04:00
return cmd.val
}
2014-07-05 14:46:27 +04:00
func (cmd *ZSliceCmd) Result() ([]Z, error) {
2014-01-09 12:17:38 +04:00
return cmd.val, cmd.err
}
2014-07-05 14:46:27 +04:00
func (cmd *ZSliceCmd) String() string {
2014-01-09 12:17:38 +04:00
return cmdString(cmd, cmd.val)
}
func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
2018-08-02 14:48:46 +03:00
// If the n is 0, can't continue reading.
if n == 0 {
cmd.val = make([]Z, 0)
return nil
}
2018-08-02 14:48:46 +03:00
typ, err := rd.PeekReplyType()
if err != nil {
return err
}
array := typ == proto.RespArray
if array {
cmd.val = make([]Z, n)
} else {
2019-06-26 14:45:38 +03:00
cmd.val = make([]Z, n/2)
}
2018-08-02 14:48:46 +03:00
for i := 0; i < len(cmd.val); i++ {
if array {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
2019-06-26 14:45:38 +03:00
}
}
2018-08-02 14:48:46 +03:00
if cmd.val[i].Member, err = rd.ReadString(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
if cmd.val[i].Score, err = rd.ReadFloat(); err != nil {
return err
}
}
return nil
2018-08-02 14:48:46 +03:00
}
2014-01-09 12:17:38 +04:00
//------------------------------------------------------------------------------
2018-10-31 16:35:23 +03:00
type ZWithKeyCmd struct {
baseCmd
2019-05-16 16:27:19 +03:00
val *ZWithKey
2018-10-31 16:35:23 +03:00
}
var _ Cmder = (*ZWithKeyCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd {
2018-10-31 16:35:23 +03:00
return &ZWithKeyCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2018-10-31 16:35:23 +03:00
}
}
func (cmd *ZWithKeyCmd) SetVal(val *ZWithKey) {
cmd.val = val
}
2019-05-16 16:27:19 +03:00
func (cmd *ZWithKeyCmd) Val() *ZWithKey {
2018-10-31 16:35:23 +03:00
return cmd.val
}
2019-05-16 16:27:19 +03:00
func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) {
2018-10-31 16:35:23 +03:00
return cmd.Val(), cmd.Err()
}
func (cmd *ZWithKeyCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) (err error) {
if err = rd.ReadFixedArrayLen(3); err != nil {
return err
}
cmd.val = &ZWithKey{}
2019-05-16 16:27:19 +03:00
if cmd.val.Key, err = rd.ReadString(); err != nil {
return err
}
if cmd.val.Member, err = rd.ReadString(); err != nil {
return err
}
if cmd.val.Score, err = rd.ReadFloat(); err != nil {
return err
}
2019-06-26 14:45:38 +03:00
return nil
2018-10-31 16:35:23 +03:00
}
//------------------------------------------------------------------------------
2014-01-09 12:17:38 +04:00
type ScanCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
2014-01-09 12:17:38 +04:00
2016-04-13 11:52:47 +03:00
page []string
cursor uint64
2017-01-03 13:44:06 +03:00
2020-03-11 17:26:42 +03:00
process cmdable
2014-01-09 12:17:38 +04:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*ScanCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd {
2016-04-13 11:52:47 +03:00
return &ScanCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-03 13:44:06 +03:00
process: process,
2016-04-13 11:52:47 +03:00
}
2014-01-09 12:17:38 +04:00
}
func (cmd *ScanCmd) SetVal(page []string, cursor uint64) {
cmd.page = page
cmd.cursor = cursor
}
func (cmd *ScanCmd) Val() (keys []string, cursor uint64) {
return cmd.page, cmd.cursor
2014-01-09 12:17:38 +04:00
}
func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) {
return cmd.page, cmd.cursor, cmd.err
2014-01-09 12:17:38 +04:00
}
func (cmd *ScanCmd) String() string {
2016-04-13 11:52:47 +03:00
return cmdString(cmd, cmd.page)
2014-01-09 12:17:38 +04:00
}
func (cmd *ScanCmd) readReply(rd *proto.Reader) error {
if err := rd.ReadFixedArrayLen(2); err != nil {
return err
}
2023-01-20 14:09:00 +03:00
cursor, err := rd.ReadUint()
if err != nil {
return err
}
2023-01-20 14:09:00 +03:00
cmd.cursor = cursor
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.page = make([]string, n)
for i := 0; i < len(cmd.page); i++ {
if cmd.page[i], err = rd.ReadString(); err != nil {
return err
}
}
return nil
2013-09-29 12:06:49 +04:00
}
2015-01-24 15:12:48 +03:00
2017-01-03 13:44:06 +03:00
// Iterator creates a new ScanIterator.
func (cmd *ScanCmd) Iterator() *ScanIterator {
return &ScanIterator{
cmd: cmd,
}
}
2015-01-24 15:12:48 +03:00
//------------------------------------------------------------------------------
type ClusterNode struct {
ID string
Addr string
NetworkingMetadata map[string]string
}
type ClusterSlot struct {
2015-10-07 17:09:20 +03:00
Start int
End int
Nodes []ClusterNode
2015-01-24 15:12:48 +03:00
}
type ClusterSlotsCmd struct {
2015-01-24 15:12:48 +03:00
baseCmd
val []ClusterSlot
2015-01-24 15:12:48 +03:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*ClusterSlotsCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd {
2017-01-13 14:39:59 +03:00
return &ClusterSlotsCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2015-01-24 15:12:48 +03:00
}
func (cmd *ClusterSlotsCmd) SetVal(val []ClusterSlot) {
cmd.val = val
}
func (cmd *ClusterSlotsCmd) Val() []ClusterSlot {
2015-01-24 15:12:48 +03:00
return cmd.val
}
func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) {
2015-01-24 15:12:48 +03:00
return cmd.Val(), cmd.Err()
}
func (cmd *ClusterSlotsCmd) String() string {
2015-01-24 15:12:48 +03:00
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]ClusterSlot, n)
for i := 0; i < len(cmd.val); i++ {
n, err = rd.ReadArrayLen()
if err != nil {
return err
}
if n < 2 {
return fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n)
}
start, err := rd.ReadInt()
if err != nil {
return err
}
end, err := rd.ReadInt()
if err != nil {
return err
}
// subtract start and end.
nodes := make([]ClusterNode, n-2)
2022-06-04 15:02:53 +03:00
for j := 0; j < len(nodes); j++ {
nn, err := rd.ReadArrayLen()
2018-08-02 14:48:46 +03:00
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
2022-06-04 15:02:53 +03:00
if nn < 2 || nn > 4 {
return fmt.Errorf("got %d elements in cluster info address, expected 2, 3, or 4", n)
2018-08-02 14:48:46 +03:00
}
ip, err := rd.ReadString()
2018-08-02 14:48:46 +03:00
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
port, err := rd.ReadString()
2018-08-02 14:48:46 +03:00
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
2018-08-16 13:25:19 +03:00
nodes[j].Addr = net.JoinHostPort(ip, port)
2018-08-02 14:48:46 +03:00
2022-06-04 15:02:53 +03:00
if nn >= 3 {
id, err := rd.ReadString()
2018-08-02 14:48:46 +03:00
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
nodes[j].ID = id
2019-06-26 14:45:38 +03:00
}
2022-06-04 15:02:53 +03:00
if nn >= 4 {
2022-06-04 17:25:12 +03:00
metadataLength, err := rd.ReadMapLen()
2022-06-04 15:02:53 +03:00
if err != nil {
return err
}
networkingMetadata := make(map[string]string, metadataLength)
2022-06-04 15:02:53 +03:00
for i := 0; i < metadataLength; i++ {
2022-06-04 15:02:53 +03:00
key, err := rd.ReadString()
if err != nil {
return err
}
value, err := rd.ReadString()
if err != nil {
return err
}
networkingMetadata[key] = value
}
nodes[j].NetworkingMetadata = networkingMetadata
}
2018-08-02 14:48:46 +03:00
}
2022-06-04 15:02:53 +03:00
cmd.val[i] = ClusterSlot{
Start: int(start),
End: int(end),
Nodes: nodes,
}
}
return nil
2018-08-02 14:48:46 +03:00
}
//------------------------------------------------------------------------------
2015-10-07 17:09:20 +03:00
// GeoLocation is used with GeoAdd to add geospatial location.
type GeoLocation struct {
2015-11-14 17:36:21 +03:00
Name string
Longitude, Latitude, Dist float64
GeoHash int64
}
2015-10-07 17:09:20 +03:00
// GeoRadiusQuery is used with GeoRadius to query geospatial index.
type GeoRadiusQuery struct {
Radius float64
2015-10-07 17:09:20 +03:00
// Can be m, km, ft, or mi. Default is km.
Unit string
WithCoord bool
WithDist bool
WithGeoHash bool
Count int
2015-10-07 17:09:20 +03:00
// Can be ASC or DESC. Default is no sort order.
2017-07-19 15:32:50 +03:00
Sort string
Store string
StoreDist string
// WithCoord+WithDist+WithGeoHash
withLen int
2015-10-07 17:09:20 +03:00
}
type GeoLocationCmd struct {
baseCmd
2015-11-14 17:36:21 +03:00
q *GeoRadiusQuery
locations []GeoLocation
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*GeoLocationCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd {
return &GeoLocationCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: geoLocationArgs(q, args...),
},
q: q,
}
}
func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} {
2015-11-14 17:36:21 +03:00
args = append(args, q.Radius)
if q.Unit != "" {
args = append(args, q.Unit)
} else {
args = append(args, "km")
}
if q.WithCoord {
2017-07-19 15:32:50 +03:00
args = append(args, "withcoord")
q.withLen++
2015-11-14 17:36:21 +03:00
}
if q.WithDist {
2017-07-19 15:32:50 +03:00
args = append(args, "withdist")
q.withLen++
2015-11-14 17:36:21 +03:00
}
if q.WithGeoHash {
2017-07-19 15:32:50 +03:00
args = append(args, "withhash")
q.withLen++
2015-11-14 17:36:21 +03:00
}
if q.Count > 0 {
2017-07-19 15:32:50 +03:00
args = append(args, "count", q.Count)
2015-11-14 17:36:21 +03:00
}
if q.Sort != "" {
args = append(args, q.Sort)
}
2017-07-19 15:32:50 +03:00
if q.Store != "" {
args = append(args, "store")
args = append(args, q.Store)
}
if q.StoreDist != "" {
args = append(args, "storedist")
args = append(args, q.StoreDist)
}
return args
}
func (cmd *GeoLocationCmd) SetVal(locations []GeoLocation) {
cmd.locations = locations
}
2015-10-07 17:09:20 +03:00
func (cmd *GeoLocationCmd) Val() []GeoLocation {
return cmd.locations
}
2015-10-07 17:09:20 +03:00
func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) {
return cmd.locations, cmd.err
}
2015-10-07 17:09:20 +03:00
func (cmd *GeoLocationCmd) String() string {
return cmdString(cmd, cmd.locations)
}
2018-08-17 13:56:37 +03:00
func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
2020-09-17 12:27:16 +03:00
if err != nil {
return err
}
cmd.locations = make([]GeoLocation, n)
for i := 0; i < len(cmd.locations); i++ {
// only name
if cmd.q.withLen == 0 {
if cmd.locations[i].Name, err = rd.ReadString(); err != nil {
return err
2019-06-26 14:45:38 +03:00
}
continue
2019-06-26 14:45:38 +03:00
}
// +name
if err = rd.ReadFixedArrayLen(cmd.q.withLen + 1); err != nil {
return err
}
2018-08-02 14:48:46 +03:00
if cmd.locations[i].Name, err = rd.ReadString(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
if cmd.q.WithDist {
if cmd.locations[i].Dist, err = rd.ReadFloat(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
}
if cmd.q.WithGeoHash {
if cmd.locations[i].GeoHash, err = rd.ReadInt(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
}
if cmd.q.WithCoord {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
if cmd.locations[i].Longitude, err = rd.ReadFloat(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
if cmd.locations[i].Latitude, err = rd.ReadFloat(); err != nil {
return err
2018-08-02 14:48:46 +03:00
}
}
}
return nil
2018-08-02 14:48:46 +03:00
}
//------------------------------------------------------------------------------
// GeoSearchQuery is used for GEOSearch/GEOSearchStore command query.
type GeoSearchQuery struct {
Member string
// Latitude and Longitude when using FromLonLat option.
Longitude float64
Latitude float64
// Distance and unit when using ByRadius option.
// Can use m, km, ft, or mi. Default is km.
Radius float64
RadiusUnit string
// Height, width and unit when using ByBox option.
// Can be m, km, ft, or mi. Default is km.
BoxWidth float64
BoxHeight float64
BoxUnit string
// Can be ASC or DESC. Default is no sort order.
Sort string
Count int
CountAny bool
}
type GeoSearchLocationQuery struct {
GeoSearchQuery
WithCoord bool
WithDist bool
WithHash bool
}
type GeoSearchStoreQuery struct {
GeoSearchQuery
// When using the StoreDist option, the command stores the items in a
// sorted set populated with their distance from the center of the circle or box,
// as a floating-point number, in the same unit specified for that shape.
StoreDist bool
}
func geoSearchLocationArgs(q *GeoSearchLocationQuery, args []interface{}) []interface{} {
args = geoSearchArgs(&q.GeoSearchQuery, args)
if q.WithCoord {
args = append(args, "withcoord")
}
if q.WithDist {
args = append(args, "withdist")
}
if q.WithHash {
args = append(args, "withhash")
}
return args
}
func geoSearchArgs(q *GeoSearchQuery, args []interface{}) []interface{} {
if q.Member != "" {
args = append(args, "frommember", q.Member)
} else {
args = append(args, "fromlonlat", q.Longitude, q.Latitude)
}
if q.Radius > 0 {
if q.RadiusUnit == "" {
q.RadiusUnit = "km"
}
args = append(args, "byradius", q.Radius, q.RadiusUnit)
} else {
if q.BoxUnit == "" {
q.BoxUnit = "km"
}
args = append(args, "bybox", q.BoxWidth, q.BoxHeight, q.BoxUnit)
}
if q.Sort != "" {
args = append(args, q.Sort)
}
if q.Count > 0 {
args = append(args, "count", q.Count)
if q.CountAny {
args = append(args, "any")
}
}
return args
}
type GeoSearchLocationCmd struct {
baseCmd
opt *GeoSearchLocationQuery
val []GeoLocation
}
var _ Cmder = (*GeoSearchLocationCmd)(nil)
func NewGeoSearchLocationCmd(
ctx context.Context, opt *GeoSearchLocationQuery, args ...interface{},
) *GeoSearchLocationCmd {
return &GeoSearchLocationCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
opt: opt,
}
}
2022-08-08 07:06:04 +03:00
func (cmd *GeoSearchLocationCmd) SetVal(val []GeoLocation) {
cmd.val = val
}
func (cmd *GeoSearchLocationCmd) Val() []GeoLocation {
return cmd.val
}
func (cmd *GeoSearchLocationCmd) Result() ([]GeoLocation, error) {
return cmd.val, cmd.err
}
func (cmd *GeoSearchLocationCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *GeoSearchLocationCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]GeoLocation, n)
for i := 0; i < n; i++ {
_, err = rd.ReadArrayLen()
if err != nil {
return err
}
var loc GeoLocation
loc.Name, err = rd.ReadString()
if err != nil {
return err
}
if cmd.opt.WithDist {
loc.Dist, err = rd.ReadFloat()
if err != nil {
return err
}
}
if cmd.opt.WithHash {
loc.GeoHash, err = rd.ReadInt()
if err != nil {
return err
}
}
if cmd.opt.WithCoord {
if err = rd.ReadFixedArrayLen(2); err != nil {
return err
}
loc.Longitude, err = rd.ReadFloat()
if err != nil {
return err
}
loc.Latitude, err = rd.ReadFloat()
if err != nil {
return err
}
}
cmd.val[i] = loc
}
return nil
}
//------------------------------------------------------------------------------
type GeoPos struct {
Longitude, Latitude float64
}
2016-08-22 00:32:06 +03:00
type GeoPosCmd struct {
baseCmd
2019-06-26 14:45:38 +03:00
val []*GeoPos
2016-08-22 00:32:06 +03:00
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*GeoPosCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd {
2017-01-13 14:39:59 +03:00
return &GeoPosCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
2016-08-22 00:32:06 +03:00
}
func (cmd *GeoPosCmd) SetVal(val []*GeoPos) {
cmd.val = val
}
func (cmd *GeoPosCmd) Val() []*GeoPos {
2019-06-26 14:45:38 +03:00
return cmd.val
2016-08-22 00:32:06 +03:00
}
func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) {
2016-08-22 00:32:06 +03:00
return cmd.Val(), cmd.Err()
}
func (cmd *GeoPosCmd) String() string {
2019-06-26 14:45:38 +03:00
return cmdString(cmd, cmd.val)
2016-08-22 00:32:06 +03:00
}
2018-08-17 13:56:37 +03:00
func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]*GeoPos, n)
2019-05-16 16:27:19 +03:00
for i := 0; i < len(cmd.val); i++ {
err = rd.ReadFixedArrayLen(2)
if err != nil {
if err == Nil {
cmd.val[i] = nil
continue
2019-05-16 16:27:19 +03:00
}
return err
2018-08-02 14:48:46 +03:00
}
longitude, err := rd.ReadFloat()
if err != nil {
return err
}
latitude, err := rd.ReadFloat()
if err != nil {
return err
}
cmd.val[i] = &GeoPos{
Longitude: longitude,
Latitude: latitude,
}
}
return nil
2018-08-02 14:48:46 +03:00
}
//------------------------------------------------------------------------------
type CommandInfo struct {
Name string
Arity int8
Flags []string
ACLFlags []string
FirstKeyPos int8
LastKeyPos int8
StepCount int8
ReadOnly bool
}
type CommandsInfoCmd struct {
baseCmd
val map[string]*CommandInfo
}
2017-09-26 11:29:22 +03:00
var _ Cmder = (*CommandsInfoCmd)(nil)
2020-03-11 17:26:42 +03:00
func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd {
2017-01-13 14:39:59 +03:00
return &CommandsInfoCmd{
2020-03-11 17:26:42 +03:00
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
2017-01-13 14:39:59 +03:00
}
}
func (cmd *CommandsInfoCmd) SetVal(val map[string]*CommandInfo) {
cmd.val = val
}
func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo {
return cmd.val
}
func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) {
return cmd.Val(), cmd.Err()
}
func (cmd *CommandsInfoCmd) String() string {
return cmdString(cmd, cmd.val)
}
2018-08-17 13:56:37 +03:00
func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
2020-06-05 09:11:12 +03:00
const numArgRedis5 = 6
const numArgRedis6 = 7
2022-06-04 16:07:28 +03:00
const numArgRedis7 = 10
2020-06-05 09:11:12 +03:00
n, err := rd.ReadArrayLen()
if err != nil {
return err
2018-08-02 14:48:46 +03:00
}
cmd.val = make(map[string]*CommandInfo, n)
2018-08-02 14:48:46 +03:00
for i := 0; i < n; i++ {
nn, err := rd.ReadArrayLen()
if err != nil {
return err
}
2022-06-04 16:07:28 +03:00
switch nn {
case numArgRedis5, numArgRedis6, numArgRedis7:
// ok
default:
return fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6/7/10", nn)
}
2019-06-26 14:45:38 +03:00
cmdInfo := &CommandInfo{}
if cmdInfo.Name, err = rd.ReadString(); err != nil {
return err
}
2018-08-02 14:48:46 +03:00
arity, err := rd.ReadInt()
if err != nil {
return err
}
cmdInfo.Arity = int8(arity)
2018-08-02 14:48:46 +03:00
flagLen, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmdInfo.Flags = make([]string, flagLen)
for f := 0; f < len(cmdInfo.Flags); f++ {
2019-06-26 14:45:38 +03:00
switch s, err := rd.ReadString(); {
case err == Nil:
cmdInfo.Flags[f] = ""
2019-06-26 14:45:38 +03:00
case err != nil:
return err
2019-06-26 14:45:38 +03:00
default:
if !cmdInfo.ReadOnly && s == "readonly" {
cmdInfo.ReadOnly = true
}
cmdInfo.Flags[f] = s
2019-06-26 14:45:38 +03:00
}
}
2018-08-02 14:48:46 +03:00
firstKeyPos, err := rd.ReadInt()
if err != nil {
return err
}
cmdInfo.FirstKeyPos = int8(firstKeyPos)
2018-08-02 14:48:46 +03:00
lastKeyPos, err := rd.ReadInt()
if err != nil {
return err
2020-06-05 09:11:12 +03:00
}
cmdInfo.LastKeyPos = int8(lastKeyPos)
2020-06-05 09:11:12 +03:00
stepCount, err := rd.ReadInt()
if err != nil {
return err
}
cmdInfo.StepCount = int8(stepCount)
2020-06-05 09:11:12 +03:00
2022-06-04 16:07:28 +03:00
if nn >= numArgRedis6 {
aclFlagLen, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmdInfo.ACLFlags = make([]string, aclFlagLen)
for f := 0; f < len(cmdInfo.ACLFlags); f++ {
switch s, err := rd.ReadString(); {
case err == Nil:
cmdInfo.ACLFlags[f] = ""
case err != nil:
return err
default:
cmdInfo.ACLFlags[f] = s
}
}
}
2022-06-04 16:07:28 +03:00
if nn >= numArgRedis7 {
if err := rd.DiscardNext(); err != nil {
return err
}
if err := rd.DiscardNext(); err != nil {
return err
}
if err := rd.DiscardNext(); err != nil {
return err
}
}
cmd.val[cmdInfo.Name] = cmdInfo
}
return nil
2018-08-02 14:48:46 +03:00
}
2018-03-06 15:50:48 +03:00
//------------------------------------------------------------------------------
type cmdsInfoCache struct {
fn func(ctx context.Context) (map[string]*CommandInfo, error)
2018-05-17 15:21:51 +03:00
2018-03-06 15:50:48 +03:00
once internal.Once
cmds map[string]*CommandInfo
}
func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache {
2018-05-17 15:21:51 +03:00
return &cmdsInfoCache{
fn: fn,
}
2018-03-06 15:50:48 +03:00
}
func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) {
2018-03-06 15:50:48 +03:00
err := c.once.Do(func() error {
cmds, err := c.fn(ctx)
2018-03-06 15:50:48 +03:00
if err != nil {
return err
}
2019-11-19 13:37:26 +03:00
// Extensions have cmd names in upper case. Convert them to lower case.
for k, v := range cmds {
lower := internal.ToLower(k)
if lower != k {
cmds[lower] = v
}
}
2019-11-19 13:37:26 +03:00
2018-03-06 15:50:48 +03:00
c.cmds = cmds
return nil
})
return c.cmds, err
}
2020-06-11 10:24:04 +03:00
//------------------------------------------------------------------------------
type SlowLog struct {
2020-09-09 12:49:45 +03:00
ID int64
Time time.Time
Duration time.Duration
Args []string
// These are also optional fields emitted only by Redis 4.0 or greater:
// https://redis.io/commands/slowlog#output-format
ClientAddr string
ClientName string
2020-06-11 10:24:04 +03:00
}
type SlowLogCmd struct {
baseCmd
val []SlowLog
}
var _ Cmder = (*SlowLogCmd)(nil)
func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd {
return &SlowLogCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
func (cmd *SlowLogCmd) SetVal(val []SlowLog) {
cmd.val = val
}
2020-06-11 10:24:04 +03:00
func (cmd *SlowLogCmd) Val() []SlowLog {
return cmd.val
}
func (cmd *SlowLogCmd) Result() ([]SlowLog, error) {
return cmd.Val(), cmd.Err()
}
func (cmd *SlowLogCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]SlowLog, n)
for i := 0; i < len(cmd.val); i++ {
nn, err := rd.ReadArrayLen()
if err != nil {
return err
}
if nn < 4 {
return fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", nn)
}
2020-06-11 10:24:04 +03:00
if cmd.val[i].ID, err = rd.ReadInt(); err != nil {
return err
}
createdAt, err := rd.ReadInt()
if err != nil {
return err
}
cmd.val[i].Time = time.Unix(createdAt, 0)
costs, err := rd.ReadInt()
if err != nil {
return err
}
cmd.val[i].Duration = time.Duration(costs) * time.Microsecond
2020-06-11 10:24:04 +03:00
cmdLen, err := rd.ReadArrayLen()
if err != nil {
return err
}
if cmdLen < 1 {
return fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen)
}
cmd.val[i].Args = make([]string, cmdLen)
for f := 0; f < len(cmd.val[i].Args); f++ {
cmd.val[i].Args[f], err = rd.ReadString()
2020-06-11 10:24:04 +03:00
if err != nil {
return err
2020-06-11 10:24:04 +03:00
}
}
2020-06-11 10:24:04 +03:00
if nn >= 5 {
if cmd.val[i].ClientAddr, err = rd.ReadString(); err != nil {
return err
2020-06-11 10:24:04 +03:00
}
}
2020-09-09 12:49:45 +03:00
if nn >= 6 {
if cmd.val[i].ClientName, err = rd.ReadString(); err != nil {
return err
2020-06-11 10:24:04 +03:00
}
}
}
2020-09-09 12:49:45 +03:00
return nil
}
//-----------------------------------------------------------------------
type MapStringInterfaceCmd struct {
baseCmd
val map[string]interface{}
}
var _ Cmder = (*MapStringInterfaceCmd)(nil)
func NewMapStringInterfaceCmd(ctx context.Context, args ...interface{}) *MapStringInterfaceCmd {
return &MapStringInterfaceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
2022-08-08 07:06:04 +03:00
func (cmd *MapStringInterfaceCmd) SetVal(val map[string]interface{}) {
cmd.val = val
}
func (cmd *MapStringInterfaceCmd) Val() map[string]interface{} {
return cmd.val
}
func (cmd *MapStringInterfaceCmd) Result() (map[string]interface{}, error) {
return cmd.Val(), cmd.Err()
}
func (cmd *MapStringInterfaceCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *MapStringInterfaceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val = make(map[string]interface{}, n)
for i := 0; i < n; i++ {
k, err := rd.ReadString()
if err != nil {
return err
}
v, err := rd.ReadReply()
if err != nil {
if err == Nil {
cmd.val[k] = Nil
continue
2020-06-11 10:24:04 +03:00
}
if err, ok := err.(proto.RedisError); ok {
cmd.val[k] = err
continue
2020-06-11 10:24:04 +03:00
}
return err
}
cmd.val[k] = v
}
return nil
}
2020-09-09 12:49:45 +03:00
//-----------------------------------------------------------------------
2020-09-09 12:49:45 +03:00
type MapStringStringSliceCmd struct {
baseCmd
val []map[string]string
}
var _ Cmder = (*MapStringStringSliceCmd)(nil)
func NewMapStringStringSliceCmd(ctx context.Context, args ...interface{}) *MapStringStringSliceCmd {
return &MapStringStringSliceCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}
2022-08-08 07:06:04 +03:00
func (cmd *MapStringStringSliceCmd) SetVal(val []map[string]string) {
cmd.val = val
}
func (cmd *MapStringStringSliceCmd) Val() []map[string]string {
return cmd.val
}
func (cmd *MapStringStringSliceCmd) Result() ([]map[string]string, error) {
return cmd.Val(), cmd.Err()
}
func (cmd *MapStringStringSliceCmd) String() string {
return cmdString(cmd, cmd.val)
}
func (cmd *MapStringStringSliceCmd) readReply(rd *proto.Reader) error {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}
cmd.val = make([]map[string]string, n)
for i := 0; i < n; i++ {
nn, err := rd.ReadMapLen()
if err != nil {
return err
}
cmd.val[i] = make(map[string]string, nn)
for f := 0; f < nn; f++ {
k, err := rd.ReadString()
if err != nil {
return err
2020-06-11 10:24:04 +03:00
}
2020-09-09 12:49:45 +03:00
v, err := rd.ReadString()
if err != nil {
return err
2020-06-11 10:24:04 +03:00
}
cmd.val[i][k] = v
2020-06-11 10:24:04 +03:00
}
}
return nil
2020-06-11 10:24:04 +03:00
}