mirror of
https://github.com/redis/go-redis.git
synced 2025-04-16 09:23:06 +03:00
* Add support for RedisJSON * Add optional args and tests * Add more tests * Add more tests * Add more tests and cleanups * Add docstring * update JSONArrIndex and matching texts Rename JSONArrIndexWithArgs to JSONArrIndexArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONArrTrim and matching texts Rename JSONArrTrimWithArgs to JSONArrTrimArgs change name of args struct to match function name for consistency change arg types to int (for required arg) and *int (for optional) * update JSONGetWithArgs and matching texts Rename JSONGetWithArgs to JSONGetArgs Removed Paths & Path from args - redundant and ambiguous Renamed mispelled "Indention" Changed args param from pointer to value Updated and added additional tests for formatting params * Update JSONSetMode changed mode parameter back to string, added value checking for that string * Updated JSONMSet Changed name of param struct to ...Args for consistency Updated arg list to use an array of structs not pointers as all args are mandatory (we never want to pass a nil parameter) * Updated tests for JSONMSet * Added stubbed (panicking) implementations of JSON.RESP AND JSON.DEBUG * Pre-pull request tidy up Renamed xArgs() to xWithArgs to match other Redis modules Modified params to xWithArgs functions to use a pointer for the arguments struct to match other Redis modules. Modified JSONMSet to JSONMSetArgs and added a version that takes a vararray of args to match the semantics of the native redis call Updated tests to match above changes. Renamed some variables in the json tests for consistency * Testing fixes Fixed error in JSONSetWithArgs found during tests Fixed tests for indentation * fix JSONCmdable typo (#5) * Remove deprecated command: JSONNumMultBy (#6) * Lowercase newJSONCmd (#7) --------- Co-authored-by: Nic Gibson <nic.gibson@redis.com> Co-authored-by: Nic Gibson <newt+github@noslogan.org>
691 lines
18 KiB
Go
691 lines
18 KiB
Go
package redis
|
|
|
|
import (
|
|
"context"
|
|
"encoding"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"reflect"
|
|
"runtime"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/redis/go-redis/v9/internal"
|
|
)
|
|
|
|
// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0,
|
|
// otherwise you will receive an error: (error) ERR syntax error.
|
|
// For example:
|
|
//
|
|
// rdb.Set(ctx, key, value, redis.KeepTTL)
|
|
const KeepTTL = -1
|
|
|
|
func usePrecise(dur time.Duration) bool {
|
|
return dur < time.Second || dur%time.Second != 0
|
|
}
|
|
|
|
func formatMs(ctx context.Context, dur time.Duration) int64 {
|
|
if dur > 0 && dur < time.Millisecond {
|
|
internal.Logger.Printf(
|
|
ctx,
|
|
"specified duration is %s, but minimal supported value is %s - truncating to 1ms",
|
|
dur, time.Millisecond,
|
|
)
|
|
return 1
|
|
}
|
|
return int64(dur / time.Millisecond)
|
|
}
|
|
|
|
func formatSec(ctx context.Context, dur time.Duration) int64 {
|
|
if dur > 0 && dur < time.Second {
|
|
internal.Logger.Printf(
|
|
ctx,
|
|
"specified duration is %s, but minimal supported value is %s - truncating to 1s",
|
|
dur, time.Second,
|
|
)
|
|
return 1
|
|
}
|
|
return int64(dur / time.Second)
|
|
}
|
|
|
|
func appendArgs(dst, src []interface{}) []interface{} {
|
|
if len(src) == 1 {
|
|
return appendArg(dst, src[0])
|
|
}
|
|
|
|
dst = append(dst, src...)
|
|
return dst
|
|
}
|
|
|
|
func appendArg(dst []interface{}, arg interface{}) []interface{} {
|
|
switch arg := arg.(type) {
|
|
case []string:
|
|
for _, s := range arg {
|
|
dst = append(dst, s)
|
|
}
|
|
return dst
|
|
case []interface{}:
|
|
dst = append(dst, arg...)
|
|
return dst
|
|
case map[string]interface{}:
|
|
for k, v := range arg {
|
|
dst = append(dst, k, v)
|
|
}
|
|
return dst
|
|
case map[string]string:
|
|
for k, v := range arg {
|
|
dst = append(dst, k, v)
|
|
}
|
|
return dst
|
|
case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP:
|
|
return append(dst, arg)
|
|
default:
|
|
// scan struct field
|
|
v := reflect.ValueOf(arg)
|
|
if v.Type().Kind() == reflect.Ptr {
|
|
if v.IsNil() {
|
|
// error: arg is not a valid object
|
|
return dst
|
|
}
|
|
v = v.Elem()
|
|
}
|
|
|
|
if v.Type().Kind() == reflect.Struct {
|
|
return appendStructField(dst, v)
|
|
}
|
|
|
|
return append(dst, arg)
|
|
}
|
|
}
|
|
|
|
// appendStructField appends the field and value held by the structure v to dst, and returns the appended dst.
|
|
func appendStructField(dst []interface{}, v reflect.Value) []interface{} {
|
|
typ := v.Type()
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
tag := typ.Field(i).Tag.Get("redis")
|
|
if tag == "" || tag == "-" {
|
|
continue
|
|
}
|
|
name, opt, _ := strings.Cut(tag, ",")
|
|
if name == "" {
|
|
continue
|
|
}
|
|
|
|
field := v.Field(i)
|
|
|
|
// miss field
|
|
if omitEmpty(opt) && isEmptyValue(field) {
|
|
continue
|
|
}
|
|
|
|
if field.CanInterface() {
|
|
dst = append(dst, name, field.Interface())
|
|
}
|
|
}
|
|
|
|
return dst
|
|
}
|
|
|
|
func omitEmpty(opt string) bool {
|
|
for opt != "" {
|
|
var name string
|
|
name, opt, _ = strings.Cut(opt, ",")
|
|
if name == "omitempty" {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func isEmptyValue(v reflect.Value) bool {
|
|
switch v.Kind() {
|
|
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
|
|
return v.Len() == 0
|
|
case reflect.Bool:
|
|
return !v.Bool()
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
return v.Int() == 0
|
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
|
return v.Uint() == 0
|
|
case reflect.Float32, reflect.Float64:
|
|
return v.Float() == 0
|
|
case reflect.Interface, reflect.Pointer:
|
|
return v.IsNil()
|
|
}
|
|
return false
|
|
}
|
|
|
|
type Cmdable interface {
|
|
Pipeline() Pipeliner
|
|
Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
|
|
|
TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error)
|
|
TxPipeline() Pipeliner
|
|
|
|
Command(ctx context.Context) *CommandsInfoCmd
|
|
CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd
|
|
CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd
|
|
CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd
|
|
ClientGetName(ctx context.Context) *StringCmd
|
|
Echo(ctx context.Context, message interface{}) *StringCmd
|
|
Ping(ctx context.Context) *StatusCmd
|
|
Quit(ctx context.Context) *StatusCmd
|
|
Unlink(ctx context.Context, keys ...string) *IntCmd
|
|
|
|
BgRewriteAOF(ctx context.Context) *StatusCmd
|
|
BgSave(ctx context.Context) *StatusCmd
|
|
ClientKill(ctx context.Context, ipPort string) *StatusCmd
|
|
ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd
|
|
ClientList(ctx context.Context) *StringCmd
|
|
ClientInfo(ctx context.Context) *ClientInfoCmd
|
|
ClientPause(ctx context.Context, dur time.Duration) *BoolCmd
|
|
ClientUnpause(ctx context.Context) *BoolCmd
|
|
ClientID(ctx context.Context) *IntCmd
|
|
ClientUnblock(ctx context.Context, id int64) *IntCmd
|
|
ClientUnblockWithError(ctx context.Context, id int64) *IntCmd
|
|
ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd
|
|
ConfigResetStat(ctx context.Context) *StatusCmd
|
|
ConfigSet(ctx context.Context, parameter, value string) *StatusCmd
|
|
ConfigRewrite(ctx context.Context) *StatusCmd
|
|
DBSize(ctx context.Context) *IntCmd
|
|
FlushAll(ctx context.Context) *StatusCmd
|
|
FlushAllAsync(ctx context.Context) *StatusCmd
|
|
FlushDB(ctx context.Context) *StatusCmd
|
|
FlushDBAsync(ctx context.Context) *StatusCmd
|
|
Info(ctx context.Context, section ...string) *StringCmd
|
|
LastSave(ctx context.Context) *IntCmd
|
|
Save(ctx context.Context) *StatusCmd
|
|
Shutdown(ctx context.Context) *StatusCmd
|
|
ShutdownSave(ctx context.Context) *StatusCmd
|
|
ShutdownNoSave(ctx context.Context) *StatusCmd
|
|
SlaveOf(ctx context.Context, host, port string) *StatusCmd
|
|
SlowLogGet(ctx context.Context, num int64) *SlowLogCmd
|
|
Time(ctx context.Context) *TimeCmd
|
|
DebugObject(ctx context.Context, key string) *StringCmd
|
|
|
|
MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd
|
|
|
|
ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd
|
|
|
|
ACLCmdable
|
|
HashCmdable
|
|
HyperLogLogCmdable
|
|
GeoCmdable
|
|
GenericCmdable
|
|
ListCmdable
|
|
SetCmdable
|
|
SortedSetCmdable
|
|
ClusterCmdable
|
|
ScriptingFunctionsCmdable
|
|
StringCmdable
|
|
PubSubCmdable
|
|
StreamCmdable
|
|
GearsCmdable
|
|
ProbabilisticCmdable
|
|
TimeseriesCmdable
|
|
JSONCmdable
|
|
}
|
|
|
|
type StatefulCmdable interface {
|
|
Cmdable
|
|
Auth(ctx context.Context, password string) *StatusCmd
|
|
AuthACL(ctx context.Context, username, password string) *StatusCmd
|
|
Select(ctx context.Context, index int) *StatusCmd
|
|
SwapDB(ctx context.Context, index1, index2 int) *StatusCmd
|
|
ClientSetName(ctx context.Context, name string) *BoolCmd
|
|
ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd
|
|
Hello(ctx context.Context, ver int, username, password, clientName string) *MapStringInterfaceCmd
|
|
}
|
|
|
|
var (
|
|
_ Cmdable = (*Client)(nil)
|
|
_ Cmdable = (*Tx)(nil)
|
|
_ Cmdable = (*Ring)(nil)
|
|
_ Cmdable = (*ClusterClient)(nil)
|
|
)
|
|
|
|
type cmdable func(ctx context.Context, cmd Cmder) error
|
|
|
|
type statefulCmdable func(ctx context.Context, cmd Cmder) error
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
func (c statefulCmdable) Auth(ctx context.Context, password string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "auth", password)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// AuthACL Perform an AUTH command, using the given user and pass.
|
|
// Should be used to authenticate the current connection with one of the connections defined in the ACL list
|
|
// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
|
|
func (c statefulCmdable) AuthACL(ctx context.Context, username, password string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "auth", username, password)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Wait(ctx context.Context, numSlaves int, timeout time.Duration) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "wait", numSlaves, int(timeout/time.Millisecond))
|
|
cmd.setReadTimeout(timeout)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) WaitAOF(ctx context.Context, numLocal, numSlaves int, timeout time.Duration) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "waitAOF", numLocal, numSlaves, int(timeout/time.Millisecond))
|
|
cmd.setReadTimeout(timeout)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c statefulCmdable) Select(ctx context.Context, index int) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "select", index)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c statefulCmdable) SwapDB(ctx context.Context, index1, index2 int) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "swapdb", index1, index2)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// ClientSetName assigns a name to the connection.
|
|
func (c statefulCmdable) ClientSetName(ctx context.Context, name string) *BoolCmd {
|
|
cmd := NewBoolCmd(ctx, "client", "setname", name)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// ClientSetInfo sends a CLIENT SETINFO command with the provided info.
|
|
func (c statefulCmdable) ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd {
|
|
err := info.Validate()
|
|
if err != nil {
|
|
panic(err.Error())
|
|
}
|
|
|
|
var cmd *StatusCmd
|
|
if info.LibName != nil {
|
|
libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, runtime.Version())
|
|
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName)
|
|
} else {
|
|
cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer)
|
|
}
|
|
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// Validate checks if only one field in the struct is non-nil.
|
|
func (info LibraryInfo) Validate() error {
|
|
if info.LibName != nil && info.LibVer != nil {
|
|
return errors.New("both LibName and LibVer cannot be set at the same time")
|
|
}
|
|
if info.LibName == nil && info.LibVer == nil {
|
|
return errors.New("at least one of LibName and LibVer should be set")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Hello Set the resp protocol used.
|
|
func (c statefulCmdable) Hello(ctx context.Context,
|
|
ver int, username, password, clientName string,
|
|
) *MapStringInterfaceCmd {
|
|
args := make([]interface{}, 0, 7)
|
|
args = append(args, "hello", ver)
|
|
if password != "" {
|
|
if username != "" {
|
|
args = append(args, "auth", username, password)
|
|
} else {
|
|
args = append(args, "auth", "default", password)
|
|
}
|
|
}
|
|
if clientName != "" {
|
|
args = append(args, "setname", clientName)
|
|
}
|
|
cmd := NewMapStringInterfaceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd {
|
|
cmd := NewCommandsInfoCmd(ctx, "command")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// FilterBy is used for the `CommandList` command parameter.
|
|
type FilterBy struct {
|
|
Module string
|
|
ACLCat string
|
|
Pattern string
|
|
}
|
|
|
|
func (c cmdable) CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd {
|
|
args := make([]interface{}, 0, 5)
|
|
args = append(args, "command", "list")
|
|
if filter != nil {
|
|
if filter.Module != "" {
|
|
args = append(args, "filterby", "module", filter.Module)
|
|
} else if filter.ACLCat != "" {
|
|
args = append(args, "filterby", "aclcat", filter.ACLCat)
|
|
} else if filter.Pattern != "" {
|
|
args = append(args, "filterby", "pattern", filter.Pattern)
|
|
}
|
|
}
|
|
cmd := NewStringSliceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd {
|
|
args := make([]interface{}, 2+len(commands))
|
|
args[0] = "command"
|
|
args[1] = "getkeys"
|
|
copy(args[2:], commands)
|
|
cmd := NewStringSliceCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd {
|
|
args := make([]interface{}, 2+len(commands))
|
|
args[0] = "command"
|
|
args[1] = "getkeysandflags"
|
|
copy(args[2:], commands)
|
|
cmd := NewKeyFlagsCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// ClientGetName returns the name of the connection.
|
|
func (c cmdable) ClientGetName(ctx context.Context) *StringCmd {
|
|
cmd := NewStringCmd(ctx, "client", "getname")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd {
|
|
cmd := NewStringCmd(ctx, "echo", message)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Ping(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "ping")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Quit(_ context.Context) *StatusCmd {
|
|
panic("not implemented")
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
func (c cmdable) BgRewriteAOF(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "bgrewriteaof")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) BgSave(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "bgsave")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientKill(ctx context.Context, ipPort string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "client", "kill", ipPort)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// ClientKillByFilter is new style syntax, while the ClientKill is old
|
|
//
|
|
// CLIENT KILL <option> [value] ... <option> [value]
|
|
func (c cmdable) ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd {
|
|
args := make([]interface{}, 2+len(keys))
|
|
args[0] = "client"
|
|
args[1] = "kill"
|
|
for i, key := range keys {
|
|
args[2+i] = key
|
|
}
|
|
cmd := NewIntCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientList(ctx context.Context) *StringCmd {
|
|
cmd := NewStringCmd(ctx, "client", "list")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientPause(ctx context.Context, dur time.Duration) *BoolCmd {
|
|
cmd := NewBoolCmd(ctx, "client", "pause", formatMs(ctx, dur))
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientUnpause(ctx context.Context) *BoolCmd {
|
|
cmd := NewBoolCmd(ctx, "client", "unpause")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientID(ctx context.Context) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "client", "id")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientUnblock(ctx context.Context, id int64) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "client", "unblock", id)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientUnblockWithError(ctx context.Context, id int64) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "client", "unblock", id, "error")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ClientInfo(ctx context.Context) *ClientInfoCmd {
|
|
cmd := NewClientInfoCmd(ctx, "client", "info")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
func (c cmdable) ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd {
|
|
cmd := NewMapStringStringCmd(ctx, "config", "get", parameter)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ConfigResetStat(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "config", "resetstat")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ConfigSet(ctx context.Context, parameter, value string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "config", "set", parameter, value)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) ConfigRewrite(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "config", "rewrite")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) DBSize(ctx context.Context) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "dbsize")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FlushAll(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "flushall")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FlushAllAsync(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "flushall", "async")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FlushDB(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "flushdb")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) FlushDBAsync(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "flushdb", "async")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Info(ctx context.Context, sections ...string) *StringCmd {
|
|
args := make([]interface{}, 1+len(sections))
|
|
args[0] = "info"
|
|
for i, section := range sections {
|
|
args[i+1] = section
|
|
}
|
|
cmd := NewStringCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) LastSave(ctx context.Context) *IntCmd {
|
|
cmd := NewIntCmd(ctx, "lastsave")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Save(ctx context.Context) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "save")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) shutdown(ctx context.Context, modifier string) *StatusCmd {
|
|
var args []interface{}
|
|
if modifier == "" {
|
|
args = []interface{}{"shutdown"}
|
|
} else {
|
|
args = []interface{}{"shutdown", modifier}
|
|
}
|
|
cmd := NewStatusCmd(ctx, args...)
|
|
_ = c(ctx, cmd)
|
|
if err := cmd.Err(); err != nil {
|
|
if err == io.EOF {
|
|
// Server quit as expected.
|
|
cmd.err = nil
|
|
}
|
|
} else {
|
|
// Server did not quit. String reply contains the reason.
|
|
cmd.err = errors.New(cmd.val)
|
|
cmd.val = ""
|
|
}
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Shutdown(ctx context.Context) *StatusCmd {
|
|
return c.shutdown(ctx, "")
|
|
}
|
|
|
|
func (c cmdable) ShutdownSave(ctx context.Context) *StatusCmd {
|
|
return c.shutdown(ctx, "save")
|
|
}
|
|
|
|
func (c cmdable) ShutdownNoSave(ctx context.Context) *StatusCmd {
|
|
return c.shutdown(ctx, "nosave")
|
|
}
|
|
|
|
func (c cmdable) SlaveOf(ctx context.Context, host, port string) *StatusCmd {
|
|
cmd := NewStatusCmd(ctx, "slaveof", host, port)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) SlowLogGet(ctx context.Context, num int64) *SlowLogCmd {
|
|
cmd := NewSlowLogCmd(context.Background(), "slowlog", "get", num)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) Sync(_ context.Context) {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (c cmdable) Time(ctx context.Context) *TimeCmd {
|
|
cmd := NewTimeCmd(ctx, "time")
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) DebugObject(ctx context.Context, key string) *StringCmd {
|
|
cmd := NewStringCmd(ctx, "debug", "object", key)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
func (c cmdable) MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd {
|
|
args := []interface{}{"memory", "usage", key}
|
|
if len(samples) > 0 {
|
|
if len(samples) != 1 {
|
|
panic("MemoryUsage expects single sample count")
|
|
}
|
|
args = append(args, "SAMPLES", samples[0])
|
|
}
|
|
cmd := NewIntCmd(ctx, args...)
|
|
cmd.SetFirstKeyPos(2)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// ModuleLoadexConfig struct is used to specify the arguments for the MODULE LOADEX command of redis.
|
|
// `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]`
|
|
type ModuleLoadexConfig struct {
|
|
Path string
|
|
Conf map[string]interface{}
|
|
Args []interface{}
|
|
}
|
|
|
|
func (c *ModuleLoadexConfig) toArgs() []interface{} {
|
|
args := make([]interface{}, 3, 3+len(c.Conf)*3+len(c.Args)*2)
|
|
args[0] = "MODULE"
|
|
args[1] = "LOADEX"
|
|
args[2] = c.Path
|
|
for k, v := range c.Conf {
|
|
args = append(args, "CONFIG", k, v)
|
|
}
|
|
for _, arg := range c.Args {
|
|
args = append(args, "ARGS", arg)
|
|
}
|
|
return args
|
|
}
|
|
|
|
// ModuleLoadex Redis `MODULE LOADEX path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]` command.
|
|
func (c cmdable) ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd {
|
|
cmd := NewStringCmd(ctx, conf.toArgs()...)
|
|
_ = c(ctx, cmd)
|
|
return cmd
|
|
}
|