@@ -14,6 +14,7 @@ import (
1414 "github.com/redis/go-redis/v9/internal"
1515 "github.com/redis/go-redis/v9/internal/hscan"
1616 "github.com/redis/go-redis/v9/internal/proto"
17+ "github.com/redis/go-redis/v9/internal/routing"
1718 "github.com/redis/go-redis/v9/internal/util"
1819)
1920
@@ -3413,6 +3414,7 @@ type CommandInfo struct {
34133414 LastKeyPos int8
34143415 StepCount int8
34153416 ReadOnly bool
3417+ Tips map [string ]string
34163418}
34173419
34183420type CommandsInfoCmd struct {
@@ -3451,7 +3453,7 @@ func (cmd *CommandsInfoCmd) String() string {
34513453func (cmd * CommandsInfoCmd ) readReply (rd * proto.Reader ) error {
34523454 const numArgRedis5 = 6
34533455 const numArgRedis6 = 7
3454- const numArgRedis7 = 10
3456+ const numArgRedis7 = 10 // Also matches redis 8
34553457
34563458 n , err := rd .ReadArrayLen ()
34573459 if err != nil {
@@ -3539,9 +3541,34 @@ func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
35393541 }
35403542
35413543 if nn >= numArgRedis7 {
3542- if err := rd .DiscardNext (); err != nil {
3544+ // The 8th argument is an array of tips.
3545+ tipsLen , err := rd .ReadArrayLen ()
3546+ if err != nil {
35433547 return err
35443548 }
3549+
3550+ cmdInfo .Tips = make (map [string ]string , tipsLen )
3551+
3552+ for f := 0 ; f < tipsLen ; f ++ {
3553+ tip , err := rd .ReadString ()
3554+ if err != nil {
3555+ return err
3556+ }
3557+
3558+ // Handle tips that don't have a colon (like "nondeterministic_output")
3559+ if ! strings .Contains (tip , ":" ) {
3560+ cmdInfo .Tips [tip ] = ""
3561+ continue
3562+ }
3563+
3564+ // Handle normal key:value tips
3565+ k , v , ok := strings .Cut (tip , ":" )
3566+ if ! ok {
3567+ return fmt .Errorf ("redis: unexpected tip %q in COMMAND reply" , tip )
3568+ }
3569+ cmdInfo .Tips [k ] = v
3570+ }
3571+
35453572 if err := rd .DiscardNext (); err != nil {
35463573 return err
35473574 }
@@ -3592,6 +3619,50 @@ func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error
35923619 return c .cmds , err
35933620}
35943621
3622+ // ------------------------------------------------------------------------------
3623+ var BuiltinPolicies = map [string ]routing.CommandPolicy {
3624+ "ft.create" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3625+ "ft.alter" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3626+ "ft.drop" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3627+
3628+ "mset" : {Request : routing .ReqMultiShard , Response : routing .RespAllSucceeded },
3629+ "mget" : {Request : routing .ReqMultiShard , Response : routing .RespSpecial },
3630+ "del" : {Request : routing .ReqMultiShard , Response : routing .RespAggSum },
3631+ }
3632+
3633+ func newCommandPolicies (commandInfo map [string ]* CommandInfo ) map [string ]routing.CommandPolicy {
3634+
3635+ table := make (map [string ]routing.CommandPolicy , len (commandInfo ))
3636+
3637+ for name , info := range commandInfo {
3638+ req := routing .ReqDefault
3639+ resp := routing .RespAllSucceeded
3640+
3641+ if tips := info .Tips ; tips != nil {
3642+ if v , ok := tips ["request_policy" ]; ok {
3643+ if p , err := routing .ParseRequestPolicy (v ); err == nil {
3644+ req = p
3645+ }
3646+ }
3647+ if v , ok := tips ["response_policy" ]; ok {
3648+ if p , err := routing .ParseResponsePolicy (v ); err == nil {
3649+ resp = p
3650+ }
3651+ }
3652+ } else {
3653+ return BuiltinPolicies
3654+ }
3655+ table [name ] = routing.CommandPolicy {Request : req , Response : resp }
3656+ }
3657+
3658+ if len (table ) == 0 {
3659+ for k , v := range BuiltinPolicies {
3660+ table [k ] = v
3661+ }
3662+ }
3663+ return table
3664+ }
3665+
35953666//------------------------------------------------------------------------------
35963667
35973668type SlowLog struct {
0 commit comments