Skip to content

Commit

Permalink
Implement LPOS: validate arguments outside transaction
Browse files Browse the repository at this point in the history
  • Loading branch information
ianstarz committed Aug 8, 2022
1 parent 9937d4a commit c7dd52d
Showing 1 changed file with 59 additions and 53 deletions.
112 changes: 59 additions & 53 deletions cmd_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,16 +168,71 @@ func (m *Miniredis) cmdLindex(c *server.Peer, cmd string, args []string) {

// LPOS key element [RANK rank] [COUNT num-matches] [MAXLEN len]
func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) {
if !m.handleAuth(c) {
return
}
if m.checkPubsub(c, cmd) {
return
}

if len(args) == 1 {
setDirty(c)
c.WriteError(errWrongNumber(cmd))
return
}
if !m.handleAuth(c) {

// Extract options from arguments if present.
//
// Redis allows duplicate options and uses the last specified.
// `LPOS key term RANK 1 RANK 2` is effectively the same as
// `LPOS key term RANK 2`
// Default options.
if len(args)%2 == 1 {
setDirty(c)
c.WriteError(msgSyntaxError)
return
}
if m.checkPubsub(c, cmd) {
return
rank, count := 1, 1 // Default values
var maxlen int // Default value is the list length (see below)
var countSpecified, maxlenSpecified bool
if len(args) > 2 {
for i := 2; i < len(args); i++ {
if i%2 == 0 {
val := args[i+1]
var err error
switch strings.ToLower(args[i]) {
case "rank":
if rank, err = strconv.Atoi(val); err != nil {
setDirty(c)
c.WriteError(msgInvalidInt)
return
}
if rank == 0 {
setDirty(c)
c.WriteError(msgRankIsZero)
return
}
case "count":
countSpecified = true
if count, err = strconv.Atoi(val); err != nil || count < 0 {
setDirty(c)
c.WriteError(msgCountIsNegative)
return
}
case "maxlen":
maxlenSpecified = true
if maxlen, err = strconv.Atoi(val); err != nil || maxlen < 0 {
setDirty(c)
c.WriteError(msgMaxLengthIsNegative)
return
}
default:
setDirty(c)
c.WriteError(msgSyntaxError)
return
}
}
}
}

withTx(m, c, func(c *server.Peer, ctx *connCtx) {
Expand All @@ -195,55 +250,6 @@ func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) {
}
l := db.listKeys[key]

// Default options.
rank := 1
count := 1
maxlen := len(l)

// Extract options from arguments if present.
//
// Redis allows duplicate options and uses the last specified.
// `LPOS key term RANK 1 RANK 2` is effectively the same as
// `LPOS key term RANK 2`
if len(args)%2 == 1 {
c.WriteError(msgSyntaxError)
return
}
var countSpecified bool
if len(args) > 2 {
for i := 2; i < len(args); i++ {
if i%2 == 0 {
val := args[i+1]
var err error
switch strings.ToLower(args[i]) {
case "rank":
if rank, err = strconv.Atoi(val); err != nil {
c.WriteError(msgInvalidInt)
return
}
if rank == 0 {
c.WriteError(msgRankIsZero)
return
}
case "count":
countSpecified = true
if count, err = strconv.Atoi(val); err != nil || count < 0 {
c.WriteError(msgCountIsNegative)
return
}
case "maxlen":
if maxlen, err = strconv.Atoi(val); err != nil || maxlen < 0 {
c.WriteError(msgMaxLengthIsNegative)
return
}
default:
c.WriteError(msgSyntaxError)
return
}
}
}
}

// RANK cannot be zero (see above).
// If RANK is positive search forward (left to right).
// If RANK is negative search backward (right to left).
Expand All @@ -252,7 +258,7 @@ func (m *Miniredis) cmdLpos(c *server.Peer, cmd string, args []string) {
comparisons := len(l)
// Only use max length if specified, not zero, and less than total length.
// When max length is specified, but is zero, this means "unlimited".
if maxlen != 0 && maxlen < len(l) {
if maxlenSpecified && maxlen != 0 && maxlen < len(l) {
comparisons = maxlen
}
if rank > 0 {
Expand Down

0 comments on commit c7dd52d

Please sign in to comment.