forked from mirror/ledisdb
Add support for brpoplpush and rpoplpush commands (#295)
This commit is contained in:
parent
5835ab9b2b
commit
0b1b6de120
|
@ -61,12 +61,14 @@ Most of the Ledisdb's commands are the same as Redis's, you can see the redis co
|
||||||
- [List](#list)
|
- [List](#list)
|
||||||
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
|
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
|
||||||
- [BRPOP key [key ...] timeout](#brpop-key-key--timeout)
|
- [BRPOP key [key ...] timeout](#brpop-key-key--timeout)
|
||||||
|
- [BRPOPLPUSH source destination timeout](#brpoplpush-source-destination-timeout)
|
||||||
- [LINDEX key index](#lindex-key-index)
|
- [LINDEX key index](#lindex-key-index)
|
||||||
- [LLEN key](#llen-key)
|
- [LLEN key](#llen-key)
|
||||||
- [LPOP key](#lpop-key)
|
- [LPOP key](#lpop-key)
|
||||||
- [LRANGE key start stop](#lrange-key-start-stop)
|
- [LRANGE key start stop](#lrange-key-start-stop)
|
||||||
- [LPUSH key value [value ...]](#lpush-key-value-value-)
|
- [LPUSH key value [value ...]](#lpush-key-value-value-)
|
||||||
- [RPOP key](#rpop-key)
|
- [RPOP key](#rpop-key)
|
||||||
|
- [RPOPLPUSH source destination](#rpoplpush-source-destination)
|
||||||
- [RPUSH key value [value ...]](#rpush-key-value-value-)
|
- [RPUSH key value [value ...]](#rpush-key-value-value-)
|
||||||
- [LCLEAR key](#lclear-key)
|
- [LCLEAR key](#lclear-key)
|
||||||
- [LMCLEAR key [key ...]](#lmclear-key-key-)
|
- [LMCLEAR key [key ...]](#lmclear-key-key-)
|
||||||
|
@ -950,6 +952,12 @@ ledis> BLPOP list1 list2 0
|
||||||
|
|
||||||
See [BLPOP key [key ...] timeout](#blpop-key-key--timeout) for more information.
|
See [BLPOP key [key ...] timeout](#blpop-key-key--timeout) for more information.
|
||||||
|
|
||||||
|
### BRPOPLPUSH source destination timeout
|
||||||
|
BRPOPLPUSH is the blocking variant of [RPOPLPUSH](#rpoplpush-source-destination).
|
||||||
|
When source contains elements, this command behaves exactly like RPOPLPUSH.
|
||||||
|
Redis will block the connection until another client pushes to it or until timeout is reached.
|
||||||
|
A timeout of zero can be used to block indefinitely.
|
||||||
|
|
||||||
### LINDEX key index
|
### LINDEX key index
|
||||||
Returns the element at index index in the list stored at key. The index is zero-based, so 0 means the first element, 1 the second element and so on. Negative indices can be used to designate elements starting at the tail of the list. Here, `-1` means the last element, `-2` means the penultimate and so forth.
|
Returns the element at index index in the list stored at key. The index is zero-based, so 0 means the first element, 1 the second element and so on. Negative indices can be used to designate elements starting at the tail of the list. Here, `-1` means the last element, `-2` means the penultimate and so forth.
|
||||||
When the value at key is not a list, an error is returned.
|
When the value at key is not a list, an error is returned.
|
||||||
|
@ -1081,6 +1089,14 @@ ledis> LRANGE a 0 3
|
||||||
2) "2"
|
2) "2"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### RPOPLPUSH source destination
|
||||||
|
Atomically returns and removes the last element (tail) of the list stored at source, and pushes the element at the first element (head) of the list stored at destination.
|
||||||
|
|
||||||
|
For example: consider source holding the list a,b,c, and destination holding the list x,y,z. Executing RPOPLPUSH results in source holding a,b and destination holding c,x,y,z.
|
||||||
|
|
||||||
|
If source does not exist, the value nil is returned and no operation is performed.
|
||||||
|
If source and destination are the same, the operation is equivalent to removing the last element from the list and pushing it as first element of the list, so it can be considered as a list rotation command.
|
||||||
|
|
||||||
### RPUSH key value [value ...]
|
### RPUSH key value [value ...]
|
||||||
Insert all the specified values at the tail of the list stored at key. If key does not exist, it is created as empty list before performing the push operation. When key holds a value that is not a list, an error is returned.
|
Insert all the specified values at the tail of the list stored at key. If key does not exist, it is created as empty list before performing the push operation. When key holds a value that is not a list, an error is returned.
|
||||||
|
|
||||||
|
|
|
@ -279,6 +279,82 @@ func lParseBPopArgs(c *client) (keys [][]byte, timeout time.Duration, err error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func brpoplpushCommand(c *client) error {
|
||||||
|
source, dest, timeout, err := lParseBRPoplpushArgs(c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ay, err := c.db.BRPop([][]byte{source}, timeout)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if ay == nil {
|
||||||
|
c.resp.writeBulk(nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := ay[1].([]byte)
|
||||||
|
if !ok {
|
||||||
|
//not sure if this even possible
|
||||||
|
return ErrValue
|
||||||
|
}
|
||||||
|
if _, err := c.db.LPush(dest, data); err != nil {
|
||||||
|
c.db.RPush(source, data) //revert pop
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resp.writeBulk(data)
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func lParseBRPoplpushArgs(c *client) (source []byte, dest []byte, timeout time.Duration, err error) {
|
||||||
|
args := c.args
|
||||||
|
if len(args) != 3 {
|
||||||
|
err = ErrCmdParams
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
source = args[0]
|
||||||
|
dest = args[1]
|
||||||
|
|
||||||
|
var t float64
|
||||||
|
if t, err = strconv.ParseFloat(hack.String(args[len(args)-1]), 64); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
timeout = time.Duration(t * float64(time.Second))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func rpoplpushCommand(c *client) error {
|
||||||
|
args := c.args
|
||||||
|
if len(args) != 2 {
|
||||||
|
return ErrCmdParams
|
||||||
|
}
|
||||||
|
source, dest := args[0], args[1]
|
||||||
|
|
||||||
|
data, err := c.db.RPop(source)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if data == nil {
|
||||||
|
c.resp.writeBulk(nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := c.db.LPush(dest, data); err != nil {
|
||||||
|
c.db.RPush(source, data) //revert pop
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.resp.writeBulk(data)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func lkeyexistsCommand(c *client) error {
|
func lkeyexistsCommand(c *client) error {
|
||||||
args := c.args
|
args := c.args
|
||||||
if len(args) != 1 {
|
if len(args) != 1 {
|
||||||
|
@ -376,6 +452,8 @@ func init() {
|
||||||
register("lpush", lpushCommand)
|
register("lpush", lpushCommand)
|
||||||
register("rpop", rpopCommand)
|
register("rpop", rpopCommand)
|
||||||
register("rpush", rpushCommand)
|
register("rpush", rpushCommand)
|
||||||
|
register("brpoplpush", brpoplpushCommand)
|
||||||
|
register("rpoplpush", rpoplpushCommand)
|
||||||
|
|
||||||
//ledisdb special command
|
//ledisdb special command
|
||||||
|
|
||||||
|
|
|
@ -297,6 +297,72 @@ func TestPop(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRPopLPush(t *testing.T) {
|
||||||
|
c := getTestConn()
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
src := []byte("sr")
|
||||||
|
des := []byte("de")
|
||||||
|
|
||||||
|
if _, err := goredis.Int(c.Do("rpoplpush", src, des)); err != goredis.ErrNil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("llen", des)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 0 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err := goredis.Int(c.Do("rpush", src, 1, 2, 3, 4, 5, 6)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if n != 6 {
|
||||||
|
t.Fatal(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("rpoplpush", src, src)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 6 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("llen", src)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 6 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("rpoplpush", src, des)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 5 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("llen", src)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 5 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("llen", des)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 1 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("lpop", des)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 5 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, err := goredis.Int(c.Do("lpop", src)); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else if v != 6 {
|
||||||
|
t.Fatal(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTrim(t *testing.T) {
|
func TestTrim(t *testing.T) {
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
Loading…
Reference in New Issue