@@ -14,6 +14,7 @@ import (
14
14
"github.com/redis/go-redis/v9/internal"
15
15
"github.com/redis/go-redis/v9/internal/hscan"
16
16
"github.com/redis/go-redis/v9/internal/proto"
17
+ "github.com/redis/go-redis/v9/internal/routing"
17
18
"github.com/redis/go-redis/v9/internal/util"
18
19
)
19
20
@@ -3478,6 +3479,7 @@ type CommandInfo struct {
3478
3479
LastKeyPos int8
3479
3480
StepCount int8
3480
3481
ReadOnly bool
3482
+ Tips map [string ]string
3481
3483
}
3482
3484
3483
3485
type CommandsInfoCmd struct {
@@ -3516,7 +3518,7 @@ func (cmd *CommandsInfoCmd) String() string {
3516
3518
func (cmd * CommandsInfoCmd ) readReply (rd * proto.Reader ) error {
3517
3519
const numArgRedis5 = 6
3518
3520
const numArgRedis6 = 7
3519
- const numArgRedis7 = 10
3521
+ const numArgRedis7 = 10 // Also matches redis 8
3520
3522
3521
3523
n , err := rd .ReadArrayLen ()
3522
3524
if err != nil {
@@ -3604,9 +3606,34 @@ func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
3604
3606
}
3605
3607
3606
3608
if nn >= numArgRedis7 {
3607
- if err := rd .DiscardNext (); err != nil {
3609
+ // The 8th argument is an array of tips.
3610
+ tipsLen , err := rd .ReadArrayLen ()
3611
+ if err != nil {
3608
3612
return err
3609
3613
}
3614
+
3615
+ cmdInfo .Tips = make (map [string ]string , tipsLen )
3616
+
3617
+ for f := 0 ; f < tipsLen ; f ++ {
3618
+ tip , err := rd .ReadString ()
3619
+ if err != nil {
3620
+ return err
3621
+ }
3622
+
3623
+ // Handle tips that don't have a colon (like "nondeterministic_output")
3624
+ if ! strings .Contains (tip , ":" ) {
3625
+ cmdInfo .Tips [tip ] = ""
3626
+ continue
3627
+ }
3628
+
3629
+ // Handle normal key:value tips
3630
+ k , v , ok := strings .Cut (tip , ":" )
3631
+ if ! ok {
3632
+ return fmt .Errorf ("redis: unexpected tip %q in COMMAND reply" , tip )
3633
+ }
3634
+ cmdInfo .Tips [k ] = v
3635
+ }
3636
+
3610
3637
if err := rd .DiscardNext (); err != nil {
3611
3638
return err
3612
3639
}
@@ -3656,6 +3683,50 @@ func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error
3656
3683
return c .cmds , err
3657
3684
}
3658
3685
3686
+ // ------------------------------------------------------------------------------
3687
+ var BuiltinPolicies = map [string ]routing.CommandPolicy {
3688
+ "ft.create" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3689
+ "ft.alter" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3690
+ "ft.drop" : {Request : routing .ReqSpecial , Response : routing .RespAllSucceeded },
3691
+
3692
+ "mset" : {Request : routing .ReqMultiShard , Response : routing .RespAllSucceeded },
3693
+ "mget" : {Request : routing .ReqMultiShard , Response : routing .RespSpecial },
3694
+ "del" : {Request : routing .ReqMultiShard , Response : routing .RespAggSum },
3695
+ }
3696
+
3697
+ func newCommandPolicies (commandInfo map [string ]* CommandInfo ) map [string ]routing.CommandPolicy {
3698
+
3699
+ table := make (map [string ]routing.CommandPolicy , len (commandInfo ))
3700
+
3701
+ for name , info := range commandInfo {
3702
+ req := routing .ReqDefault
3703
+ resp := routing .RespAllSucceeded
3704
+
3705
+ if tips := info .Tips ; tips != nil {
3706
+ if v , ok := tips ["request_policy" ]; ok {
3707
+ if p , err := routing .ParseRequestPolicy (v ); err == nil {
3708
+ req = p
3709
+ }
3710
+ }
3711
+ if v , ok := tips ["response_policy" ]; ok {
3712
+ if p , err := routing .ParseResponsePolicy (v ); err == nil {
3713
+ resp = p
3714
+ }
3715
+ }
3716
+ } else {
3717
+ return BuiltinPolicies
3718
+ }
3719
+ table [name ] = routing.CommandPolicy {Request : req , Response : resp }
3720
+ }
3721
+
3722
+ if len (table ) == 0 {
3723
+ for k , v := range BuiltinPolicies {
3724
+ table [k ] = v
3725
+ }
3726
+ }
3727
+ return table
3728
+ }
3729
+
3659
3730
//------------------------------------------------------------------------------
3660
3731
3661
3732
type SlowLog struct {
0 commit comments