@@ -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
@@ -3413,6 +3414,7 @@ type CommandInfo struct {
3413
3414
LastKeyPos int8
3414
3415
StepCount int8
3415
3416
ReadOnly bool
3417
+ Tips map [string ]string
3416
3418
}
3417
3419
3418
3420
type CommandsInfoCmd struct {
@@ -3451,7 +3453,7 @@ func (cmd *CommandsInfoCmd) String() string {
3451
3453
func (cmd * CommandsInfoCmd ) readReply (rd * proto.Reader ) error {
3452
3454
const numArgRedis5 = 6
3453
3455
const numArgRedis6 = 7
3454
- const numArgRedis7 = 10
3456
+ const numArgRedis7 = 10 // Also matches redis 8
3455
3457
3456
3458
n , err := rd .ReadArrayLen ()
3457
3459
if err != nil {
@@ -3539,9 +3541,34 @@ func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error {
3539
3541
}
3540
3542
3541
3543
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 {
3543
3547
return err
3544
3548
}
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
+
3545
3572
if err := rd .DiscardNext (); err != nil {
3546
3573
return err
3547
3574
}
@@ -3592,6 +3619,50 @@ func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error
3592
3619
return c .cmds , err
3593
3620
}
3594
3621
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
+
3595
3666
//------------------------------------------------------------------------------
3596
3667
3597
3668
type SlowLog struct {
0 commit comments