forked from mirror/ledisdb
Merge branch 'develop'
This commit is contained in:
commit
9680161559
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ip = flag.String("h", "127.0.0.1", "ledisdb server ip (default 127.0.0.1)")
|
||||||
|
var port = flag.Int("p", 6380, "ledisdb server port (default 6380)")
|
||||||
|
var socket = flag.String("s", "", "ledisdb server socket, overwrite ip and port")
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
cfg := new(ledis.Config)
|
||||||
|
if len(*socket) > 0 {
|
||||||
|
cfg.Addr = *socket
|
||||||
|
} else {
|
||||||
|
cfg.Addr = fmt.Sprintf("%s:%d", *ip, *port)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.MaxIdleConns = 1
|
||||||
|
|
||||||
|
c := ledis.NewClient(cfg)
|
||||||
|
|
||||||
|
reader := bufio.NewReader(os.Stdin)
|
||||||
|
|
||||||
|
for {
|
||||||
|
fmt.Printf("ledis %s > ", cfg.Addr)
|
||||||
|
|
||||||
|
cmd, _ := reader.ReadString('\n')
|
||||||
|
|
||||||
|
cmds := strings.Fields(cmd)
|
||||||
|
if len(cmds) == 0 {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
args := make([]interface{}, len(cmds[1:]))
|
||||||
|
for i := range args {
|
||||||
|
args[i] = cmds[1+i]
|
||||||
|
}
|
||||||
|
r, err := c.Do(cmds[0], args...)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("%s", err.Error())
|
||||||
|
} else {
|
||||||
|
printReply(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func printReply(reply interface{}) {
|
||||||
|
switch reply := reply.(type) {
|
||||||
|
case int64:
|
||||||
|
fmt.Printf("(integer) %d", reply)
|
||||||
|
case string:
|
||||||
|
fmt.Printf("%q", reply)
|
||||||
|
case []byte:
|
||||||
|
fmt.Printf("%q", reply)
|
||||||
|
case nil:
|
||||||
|
fmt.Printf("(empty list or set)")
|
||||||
|
case ledis.Error:
|
||||||
|
fmt.Printf("%s", string(reply))
|
||||||
|
case []interface{}:
|
||||||
|
for i, v := range reply {
|
||||||
|
fmt.Printf("%d) ", i+1)
|
||||||
|
if v == nil {
|
||||||
|
fmt.Printf("(nil)")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%q", v)
|
||||||
|
}
|
||||||
|
if i != len(reply)-1 {
|
||||||
|
fmt.Printf("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Printf("invalid ledis reply")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ledis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pingPeriod time.Duration = 3 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Addr string
|
||||||
|
MaxIdleConns int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
sync.Mutex
|
||||||
|
|
||||||
|
cfg *Config
|
||||||
|
proto string
|
||||||
|
|
||||||
|
conns *list.List
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(cfg *Config) *Client {
|
||||||
|
c := new(Client)
|
||||||
|
|
||||||
|
c.cfg = cfg
|
||||||
|
|
||||||
|
if strings.Contains(cfg.Addr, "/") {
|
||||||
|
c.proto = "unix"
|
||||||
|
} else {
|
||||||
|
c.proto = "tcp"
|
||||||
|
}
|
||||||
|
|
||||||
|
c.conns = list.New()
|
||||||
|
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Do(cmd string, args ...interface{}) (interface{}, error) {
|
||||||
|
co := c.get()
|
||||||
|
r, err := co.Do(cmd, args...)
|
||||||
|
c.put(co)
|
||||||
|
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Close() {
|
||||||
|
c.Lock()
|
||||||
|
defer c.Unlock()
|
||||||
|
|
||||||
|
for c.conns.Len() > 0 {
|
||||||
|
e := c.conns.Front()
|
||||||
|
co := e.Value.(*Conn)
|
||||||
|
c.conns.Remove(e)
|
||||||
|
|
||||||
|
co.finalize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) Get() *Conn {
|
||||||
|
return c.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) get() *Conn {
|
||||||
|
c.Lock()
|
||||||
|
if c.conns.Len() == 0 {
|
||||||
|
c.Unlock()
|
||||||
|
|
||||||
|
return c.newConn()
|
||||||
|
} else {
|
||||||
|
e := c.conns.Front()
|
||||||
|
co := e.Value.(*Conn)
|
||||||
|
c.conns.Remove(e)
|
||||||
|
|
||||||
|
c.Unlock()
|
||||||
|
|
||||||
|
return co
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) put(conn *Conn) {
|
||||||
|
c.Lock()
|
||||||
|
if c.conns.Len() >= c.cfg.MaxIdleConns {
|
||||||
|
c.Unlock()
|
||||||
|
conn.finalize()
|
||||||
|
} else {
|
||||||
|
conn.lastActive = time.Now()
|
||||||
|
c.conns.PushFront(conn)
|
||||||
|
c.Unlock()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,309 @@
|
||||||
|
package ledis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Error represents an error returned in a command reply.
|
||||||
|
type Error string
|
||||||
|
|
||||||
|
func (err Error) Error() string { return string(err) }
|
||||||
|
|
||||||
|
type Conn struct {
|
||||||
|
client *Client
|
||||||
|
|
||||||
|
c net.Conn
|
||||||
|
br *bufio.Reader
|
||||||
|
bw *bufio.Writer
|
||||||
|
|
||||||
|
lastActive time.Time
|
||||||
|
|
||||||
|
// Scratch space for formatting argument length.
|
||||||
|
// '*' or '$', length, "\r\n"
|
||||||
|
lenScratch [32]byte
|
||||||
|
|
||||||
|
// Scratch space for formatting integers and floats.
|
||||||
|
numScratch [40]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Close() {
|
||||||
|
c.client.put(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
||||||
|
if err := c.connect(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.writeCommand(cmd, args); err != nil {
|
||||||
|
c.finalize()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.bw.Flush(); err != nil {
|
||||||
|
c.finalize()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if reply, err := c.readReply(); err != nil {
|
||||||
|
c.finalize()
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
if e, ok := reply.(Error); ok {
|
||||||
|
return reply, e
|
||||||
|
} else {
|
||||||
|
return reply, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) finalize() {
|
||||||
|
if c.c != nil {
|
||||||
|
c.c.Close()
|
||||||
|
c.c = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) connect() error {
|
||||||
|
if c.c != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
c.c, err = net.Dial(c.client.proto, c.client.cfg.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.br != nil {
|
||||||
|
c.br.Reset(c.c)
|
||||||
|
} else {
|
||||||
|
c.br = bufio.NewReader(c.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.bw != nil {
|
||||||
|
c.bw.Reset(c.c)
|
||||||
|
} else {
|
||||||
|
c.bw = bufio.NewWriter(c.c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeLen(prefix byte, n int) error {
|
||||||
|
c.lenScratch[len(c.lenScratch)-1] = '\n'
|
||||||
|
c.lenScratch[len(c.lenScratch)-2] = '\r'
|
||||||
|
i := len(c.lenScratch) - 3
|
||||||
|
for {
|
||||||
|
c.lenScratch[i] = byte('0' + n%10)
|
||||||
|
i -= 1
|
||||||
|
n = n / 10
|
||||||
|
if n == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.lenScratch[i] = prefix
|
||||||
|
_, err := c.bw.Write(c.lenScratch[i:])
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeString(s string) error {
|
||||||
|
c.writeLen('$', len(s))
|
||||||
|
c.bw.WriteString(s)
|
||||||
|
_, err := c.bw.WriteString("\r\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeBytes(p []byte) error {
|
||||||
|
c.writeLen('$', len(p))
|
||||||
|
c.bw.Write(p)
|
||||||
|
_, err := c.bw.WriteString("\r\n")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeInt64(n int64) error {
|
||||||
|
return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeFloat64(n float64) error {
|
||||||
|
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) writeCommand(cmd string, args []interface{}) (err error) {
|
||||||
|
c.writeLen('*', 1+len(args))
|
||||||
|
err = c.writeString(cmd)
|
||||||
|
for _, arg := range args {
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
switch arg := arg.(type) {
|
||||||
|
case string:
|
||||||
|
err = c.writeString(arg)
|
||||||
|
case []byte:
|
||||||
|
err = c.writeBytes(arg)
|
||||||
|
case int:
|
||||||
|
err = c.writeInt64(int64(arg))
|
||||||
|
case int64:
|
||||||
|
err = c.writeInt64(arg)
|
||||||
|
case float64:
|
||||||
|
err = c.writeFloat64(arg)
|
||||||
|
case bool:
|
||||||
|
if arg {
|
||||||
|
err = c.writeString("1")
|
||||||
|
} else {
|
||||||
|
err = c.writeString("0")
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
err = c.writeString("")
|
||||||
|
default:
|
||||||
|
var buf bytes.Buffer
|
||||||
|
fmt.Fprint(&buf, arg)
|
||||||
|
err = c.writeBytes(buf.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conn) readLine() ([]byte, error) {
|
||||||
|
p, err := c.br.ReadSlice('\n')
|
||||||
|
if err == bufio.ErrBufferFull {
|
||||||
|
return nil, errors.New("ledis: long response line")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
i := len(p) - 2
|
||||||
|
if i < 0 || p[i] != '\r' {
|
||||||
|
return nil, errors.New("ledis: bad response line terminator")
|
||||||
|
}
|
||||||
|
return p[:i], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseLen parses bulk string and array lengths.
|
||||||
|
func parseLen(p []byte) (int, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return -1, errors.New("ledis: malformed length")
|
||||||
|
}
|
||||||
|
|
||||||
|
if p[0] == '-' && len(p) == 2 && p[1] == '1' {
|
||||||
|
// handle $-1 and $-1 null replies.
|
||||||
|
return -1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int
|
||||||
|
for _, b := range p {
|
||||||
|
n *= 10
|
||||||
|
if b < '0' || b > '9' {
|
||||||
|
return -1, errors.New("ledis: illegal bytes in length")
|
||||||
|
}
|
||||||
|
n += int(b - '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseInt parses an integer reply.
|
||||||
|
func parseInt(p []byte) (interface{}, error) {
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, errors.New("ledis: malformed integer")
|
||||||
|
}
|
||||||
|
|
||||||
|
var negate bool
|
||||||
|
if p[0] == '-' {
|
||||||
|
negate = true
|
||||||
|
p = p[1:]
|
||||||
|
if len(p) == 0 {
|
||||||
|
return 0, errors.New("ledis: malformed integer")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var n int64
|
||||||
|
for _, b := range p {
|
||||||
|
n *= 10
|
||||||
|
if b < '0' || b > '9' {
|
||||||
|
return 0, errors.New("ledis: illegal bytes in length")
|
||||||
|
}
|
||||||
|
n += int64(b - '0')
|
||||||
|
}
|
||||||
|
|
||||||
|
if negate {
|
||||||
|
n = -n
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
okReply interface{} = "OK"
|
||||||
|
pongReply interface{} = "PONG"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c *Conn) readReply() (interface{}, error) {
|
||||||
|
line, err := c.readLine()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(line) == 0 {
|
||||||
|
return nil, errors.New("ledis: short response line")
|
||||||
|
}
|
||||||
|
switch line[0] {
|
||||||
|
case '+':
|
||||||
|
switch {
|
||||||
|
case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
|
||||||
|
// Avoid allocation for frequent "+OK" response.
|
||||||
|
return okReply, nil
|
||||||
|
case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
|
||||||
|
// Avoid allocation in PING command benchmarks :)
|
||||||
|
return pongReply, nil
|
||||||
|
default:
|
||||||
|
return string(line[1:]), nil
|
||||||
|
}
|
||||||
|
case '-':
|
||||||
|
return Error(string(line[1:])), nil
|
||||||
|
case ':':
|
||||||
|
return parseInt(line[1:])
|
||||||
|
case '$':
|
||||||
|
n, err := parseLen(line[1:])
|
||||||
|
if n < 0 || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p := make([]byte, n)
|
||||||
|
_, err = io.ReadFull(c.br, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if line, err := c.readLine(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if len(line) != 0 {
|
||||||
|
return nil, errors.New("ledis: bad bulk string format")
|
||||||
|
}
|
||||||
|
return p, nil
|
||||||
|
case '*':
|
||||||
|
n, err := parseLen(line[1:])
|
||||||
|
if n < 0 || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := make([]interface{}, n)
|
||||||
|
for i := range r {
|
||||||
|
r[i], err = c.readReply()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
return nil, errors.New("ledis: unexpected response line")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) newConn() *Conn {
|
||||||
|
co := new(Conn)
|
||||||
|
co.client = c
|
||||||
|
|
||||||
|
return co
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
// Copyright 2012 Gary Burd
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ledis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClient(t *testing.T) {
|
||||||
|
cfg := new(Config)
|
||||||
|
cfg.Addr = "127.0.0.1:6380"
|
||||||
|
cfg.MaxIdleConns = 4
|
||||||
|
|
||||||
|
c := NewClient(cfg)
|
||||||
|
|
||||||
|
c.Close()
|
||||||
|
}
|
|
@ -1,18 +1,4 @@
|
||||||
// Copyright 2012 Gary Burd
|
package ledis
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -21,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrNil indicates that a reply value is nil.
|
// ErrNil indicates that a reply value is nil.
|
||||||
var ErrNil = errors.New("redigo: nil returned")
|
var ErrNil = errors.New("ledis: nil returned")
|
||||||
|
|
||||||
// Int is a helper that converts a command reply to an integer. If err is not
|
// Int is a helper that converts a command reply to an integer. If err is not
|
||||||
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
|
// equal to nil, then Int returns 0, err. Otherwise, Int converts the
|
||||||
|
@ -51,7 +37,7 @@ func Int(reply interface{}, err error) (int, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return 0, reply
|
return 0, reply
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply)
|
return 0, fmt.Errorf("ledis: unexpected type for Int, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64 is a helper that converts a command reply to 64 bit integer. If err is
|
// Int64 is a helper that converts a command reply to 64 bit integer. If err is
|
||||||
|
@ -78,10 +64,10 @@ func Int64(reply interface{}, err error) (int64, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return 0, reply
|
return 0, reply
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply)
|
return 0, fmt.Errorf("ledis: unexpected type for Int64, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
var errNegativeInt = errors.New("redigo: unexpected value for Uint64")
|
var errNegativeInt = errors.New("ledis: unexpected value for Uint64")
|
||||||
|
|
||||||
// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
|
// Uint64 is a helper that converts a command reply to 64 bit integer. If err is
|
||||||
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
|
// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the
|
||||||
|
@ -110,7 +96,7 @@ func Uint64(reply interface{}, err error) (uint64, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return 0, reply
|
return 0, reply
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply)
|
return 0, fmt.Errorf("ledis: unexpected type for Uint64, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64 is a helper that converts a command reply to 64 bit float. If err is
|
// Float64 is a helper that converts a command reply to 64 bit float. If err is
|
||||||
|
@ -134,7 +120,7 @@ func Float64(reply interface{}, err error) (float64, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return 0, reply
|
return 0, reply
|
||||||
}
|
}
|
||||||
return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply)
|
return 0, fmt.Errorf("ledis: unexpected type for Float64, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// String is a helper that converts a command reply to a string. If err is not
|
// String is a helper that converts a command reply to a string. If err is not
|
||||||
|
@ -160,7 +146,7 @@ func String(reply interface{}, err error) (string, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return "", reply
|
return "", reply
|
||||||
}
|
}
|
||||||
return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply)
|
return "", fmt.Errorf("ledis: unexpected type for String, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bytes is a helper that converts a command reply to a slice of bytes. If err
|
// Bytes is a helper that converts a command reply to a slice of bytes. If err
|
||||||
|
@ -186,7 +172,7 @@ func Bytes(reply interface{}, err error) ([]byte, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return nil, reply
|
return nil, reply
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply)
|
return nil, fmt.Errorf("ledis: unexpected type for Bytes, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool is a helper that converts a command reply to a boolean. If err is not
|
// Bool is a helper that converts a command reply to a boolean. If err is not
|
||||||
|
@ -212,7 +198,7 @@ func Bool(reply interface{}, err error) (bool, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return false, reply
|
return false, reply
|
||||||
}
|
}
|
||||||
return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply)
|
return false, fmt.Errorf("ledis: unexpected type for Bool, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MultiBulk is deprecated. Use Values.
|
// MultiBulk is deprecated. Use Values.
|
||||||
|
@ -238,7 +224,7 @@ func Values(reply interface{}, err error) ([]interface{}, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return nil, reply
|
return nil, reply
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply)
|
return nil, fmt.Errorf("ledis: unexpected type for Values, got type %T", reply)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strings is a helper that converts an array command reply to a []string. If
|
// Strings is a helper that converts an array command reply to a []string. If
|
||||||
|
@ -257,7 +243,7 @@ func Strings(reply interface{}, err error) ([]string, error) {
|
||||||
}
|
}
|
||||||
p, ok := reply[i].([]byte)
|
p, ok := reply[i].([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i])
|
return nil, fmt.Errorf("ledis: unexpected element type for Strings, got type %T", reply[i])
|
||||||
}
|
}
|
||||||
result[i] = string(p)
|
result[i] = string(p)
|
||||||
}
|
}
|
||||||
|
@ -267,5 +253,5 @@ func Strings(reply interface{}, err error) ([]string, error) {
|
||||||
case Error:
|
case Error:
|
||||||
return nil, reply
|
return nil, reply
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply)
|
return nil, fmt.Errorf("ledis: unexpected type for Strings, got type %T", reply)
|
||||||
}
|
}
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright 2014 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
watchState = 1 << iota
|
|
||||||
multiState
|
|
||||||
subscribeState
|
|
||||||
monitorState
|
|
||||||
)
|
|
||||||
|
|
||||||
type commandInfo struct {
|
|
||||||
set, clear int
|
|
||||||
}
|
|
||||||
|
|
||||||
var commandInfos = map[string]commandInfo{
|
|
||||||
"WATCH": commandInfo{set: watchState},
|
|
||||||
"UNWATCH": commandInfo{clear: watchState},
|
|
||||||
"MULTI": commandInfo{set: multiState},
|
|
||||||
"EXEC": commandInfo{clear: watchState | multiState},
|
|
||||||
"DISCARD": commandInfo{clear: watchState | multiState},
|
|
||||||
"PSUBSCRIBE": commandInfo{set: subscribeState},
|
|
||||||
"SUBSCRIBE": commandInfo{set: subscribeState},
|
|
||||||
"MONITOR": commandInfo{set: monitorState},
|
|
||||||
}
|
|
||||||
|
|
||||||
func lookupCommandInfo(commandName string) commandInfo {
|
|
||||||
return commandInfos[strings.ToUpper(commandName)]
|
|
||||||
}
|
|
|
@ -1,418 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// conn is the low-level implementation of Conn
|
|
||||||
type conn struct {
|
|
||||||
|
|
||||||
// Shared
|
|
||||||
mu sync.Mutex
|
|
||||||
pending int
|
|
||||||
err error
|
|
||||||
conn net.Conn
|
|
||||||
|
|
||||||
// Read
|
|
||||||
readTimeout time.Duration
|
|
||||||
br *bufio.Reader
|
|
||||||
|
|
||||||
// Write
|
|
||||||
writeTimeout time.Duration
|
|
||||||
bw *bufio.Writer
|
|
||||||
|
|
||||||
// Scratch space for formatting argument length.
|
|
||||||
// '*' or '$', length, "\r\n"
|
|
||||||
lenScratch [32]byte
|
|
||||||
|
|
||||||
// Scratch space for formatting integers and floats.
|
|
||||||
numScratch [40]byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dial connects to the Redis server at the given network and address.
|
|
||||||
func Dial(network, address string) (Conn, error) {
|
|
||||||
c, err := net.Dial(network, address)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewConn(c, 0, 0), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DialTimeout acts like Dial but takes timeouts for establishing the
|
|
||||||
// connection to the server, writing a command and reading a reply.
|
|
||||||
func DialTimeout(network, address string, connectTimeout, readTimeout, writeTimeout time.Duration) (Conn, error) {
|
|
||||||
var c net.Conn
|
|
||||||
var err error
|
|
||||||
if connectTimeout > 0 {
|
|
||||||
c, err = net.DialTimeout(network, address, connectTimeout)
|
|
||||||
} else {
|
|
||||||
c, err = net.Dial(network, address)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return NewConn(c, readTimeout, writeTimeout), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewConn returns a new Redigo connection for the given net connection.
|
|
||||||
func NewConn(netConn net.Conn, readTimeout, writeTimeout time.Duration) Conn {
|
|
||||||
return &conn{
|
|
||||||
conn: netConn,
|
|
||||||
bw: bufio.NewWriter(netConn),
|
|
||||||
br: bufio.NewReader(netConn),
|
|
||||||
readTimeout: readTimeout,
|
|
||||||
writeTimeout: writeTimeout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Close() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
err := c.err
|
|
||||||
if c.err == nil {
|
|
||||||
c.err = errors.New("redigo: closed")
|
|
||||||
err = c.conn.Close()
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) fatal(err error) error {
|
|
||||||
c.mu.Lock()
|
|
||||||
if c.err == nil {
|
|
||||||
c.err = err
|
|
||||||
// Close connection to force errors on subsequent calls and to unblock
|
|
||||||
// other reader or writer.
|
|
||||||
c.conn.Close()
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Err() error {
|
|
||||||
c.mu.Lock()
|
|
||||||
err := c.err
|
|
||||||
c.mu.Unlock()
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeLen(prefix byte, n int) error {
|
|
||||||
c.lenScratch[len(c.lenScratch)-1] = '\n'
|
|
||||||
c.lenScratch[len(c.lenScratch)-2] = '\r'
|
|
||||||
i := len(c.lenScratch) - 3
|
|
||||||
for {
|
|
||||||
c.lenScratch[i] = byte('0' + n%10)
|
|
||||||
i -= 1
|
|
||||||
n = n / 10
|
|
||||||
if n == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.lenScratch[i] = prefix
|
|
||||||
_, err := c.bw.Write(c.lenScratch[i:])
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeString(s string) error {
|
|
||||||
c.writeLen('$', len(s))
|
|
||||||
c.bw.WriteString(s)
|
|
||||||
_, err := c.bw.WriteString("\r\n")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeBytes(p []byte) error {
|
|
||||||
c.writeLen('$', len(p))
|
|
||||||
c.bw.Write(p)
|
|
||||||
_, err := c.bw.WriteString("\r\n")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeInt64(n int64) error {
|
|
||||||
return c.writeBytes(strconv.AppendInt(c.numScratch[:0], n, 10))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeFloat64(n float64) error {
|
|
||||||
return c.writeBytes(strconv.AppendFloat(c.numScratch[:0], n, 'g', -1, 64))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) writeCommand(cmd string, args []interface{}) (err error) {
|
|
||||||
c.writeLen('*', 1+len(args))
|
|
||||||
err = c.writeString(cmd)
|
|
||||||
for _, arg := range args {
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
switch arg := arg.(type) {
|
|
||||||
case string:
|
|
||||||
err = c.writeString(arg)
|
|
||||||
case []byte:
|
|
||||||
err = c.writeBytes(arg)
|
|
||||||
case int:
|
|
||||||
err = c.writeInt64(int64(arg))
|
|
||||||
case int64:
|
|
||||||
err = c.writeInt64(arg)
|
|
||||||
case float64:
|
|
||||||
err = c.writeFloat64(arg)
|
|
||||||
case bool:
|
|
||||||
if arg {
|
|
||||||
err = c.writeString("1")
|
|
||||||
} else {
|
|
||||||
err = c.writeString("0")
|
|
||||||
}
|
|
||||||
case nil:
|
|
||||||
err = c.writeString("")
|
|
||||||
default:
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprint(&buf, arg)
|
|
||||||
err = c.writeBytes(buf.Bytes())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) readLine() ([]byte, error) {
|
|
||||||
p, err := c.br.ReadSlice('\n')
|
|
||||||
if err == bufio.ErrBufferFull {
|
|
||||||
return nil, errors.New("redigo: long response line")
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
i := len(p) - 2
|
|
||||||
if i < 0 || p[i] != '\r' {
|
|
||||||
return nil, errors.New("redigo: bad response line terminator")
|
|
||||||
}
|
|
||||||
return p[:i], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseLen parses bulk string and array lengths.
|
|
||||||
func parseLen(p []byte) (int, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return -1, errors.New("redigo: malformed length")
|
|
||||||
}
|
|
||||||
|
|
||||||
if p[0] == '-' && len(p) == 2 && p[1] == '1' {
|
|
||||||
// handle $-1 and $-1 null replies.
|
|
||||||
return -1, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var n int
|
|
||||||
for _, b := range p {
|
|
||||||
n *= 10
|
|
||||||
if b < '0' || b > '9' {
|
|
||||||
return -1, errors.New("redigo: illegal bytes in length")
|
|
||||||
}
|
|
||||||
n += int(b - '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseInt parses an integer reply.
|
|
||||||
func parseInt(p []byte) (interface{}, error) {
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, errors.New("redigo: malformed integer")
|
|
||||||
}
|
|
||||||
|
|
||||||
var negate bool
|
|
||||||
if p[0] == '-' {
|
|
||||||
negate = true
|
|
||||||
p = p[1:]
|
|
||||||
if len(p) == 0 {
|
|
||||||
return 0, errors.New("redigo: malformed integer")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var n int64
|
|
||||||
for _, b := range p {
|
|
||||||
n *= 10
|
|
||||||
if b < '0' || b > '9' {
|
|
||||||
return 0, errors.New("redigo: illegal bytes in length")
|
|
||||||
}
|
|
||||||
n += int64(b - '0')
|
|
||||||
}
|
|
||||||
|
|
||||||
if negate {
|
|
||||||
n = -n
|
|
||||||
}
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
okReply interface{} = "OK"
|
|
||||||
pongReply interface{} = "PONG"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *conn) readReply() (interface{}, error) {
|
|
||||||
line, err := c.readLine()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if len(line) == 0 {
|
|
||||||
return nil, errors.New("redigo: short response line")
|
|
||||||
}
|
|
||||||
switch line[0] {
|
|
||||||
case '+':
|
|
||||||
switch {
|
|
||||||
case len(line) == 3 && line[1] == 'O' && line[2] == 'K':
|
|
||||||
// Avoid allocation for frequent "+OK" response.
|
|
||||||
return okReply, nil
|
|
||||||
case len(line) == 5 && line[1] == 'P' && line[2] == 'O' && line[3] == 'N' && line[4] == 'G':
|
|
||||||
// Avoid allocation in PING command benchmarks :)
|
|
||||||
return pongReply, nil
|
|
||||||
default:
|
|
||||||
return string(line[1:]), nil
|
|
||||||
}
|
|
||||||
case '-':
|
|
||||||
return Error(string(line[1:])), nil
|
|
||||||
case ':':
|
|
||||||
return parseInt(line[1:])
|
|
||||||
case '$':
|
|
||||||
n, err := parseLen(line[1:])
|
|
||||||
if n < 0 || err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
p := make([]byte, n)
|
|
||||||
_, err = io.ReadFull(c.br, p)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if line, err := c.readLine(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
} else if len(line) != 0 {
|
|
||||||
return nil, errors.New("redigo: bad bulk string format")
|
|
||||||
}
|
|
||||||
return p, nil
|
|
||||||
case '*':
|
|
||||||
n, err := parseLen(line[1:])
|
|
||||||
if n < 0 || err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r := make([]interface{}, n)
|
|
||||||
for i := range r {
|
|
||||||
r[i], err = c.readReply()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
return nil, errors.New("redigo: unexpected response line")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Send(cmd string, args ...interface{}) error {
|
|
||||||
c.mu.Lock()
|
|
||||||
c.pending += 1
|
|
||||||
c.mu.Unlock()
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
if err := c.writeCommand(cmd, args); err != nil {
|
|
||||||
return c.fatal(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Flush() error {
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
if err := c.bw.Flush(); err != nil {
|
|
||||||
return c.fatal(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Receive() (reply interface{}, err error) {
|
|
||||||
c.mu.Lock()
|
|
||||||
// There can be more receives than sends when using pub/sub. To allow
|
|
||||||
// normal use of the connection after unsubscribe from all channels, do not
|
|
||||||
// decrement pending to a negative value.
|
|
||||||
if c.pending > 0 {
|
|
||||||
c.pending -= 1
|
|
||||||
}
|
|
||||||
c.mu.Unlock()
|
|
||||||
if c.readTimeout != 0 {
|
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
|
||||||
}
|
|
||||||
if reply, err = c.readReply(); err != nil {
|
|
||||||
return nil, c.fatal(err)
|
|
||||||
}
|
|
||||||
if err, ok := reply.(Error); ok {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {
|
|
||||||
c.mu.Lock()
|
|
||||||
pending := c.pending
|
|
||||||
c.pending = 0
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
if cmd == "" && pending == 0 {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.writeTimeout != 0 {
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(c.writeTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd != "" {
|
|
||||||
c.writeCommand(cmd, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := c.bw.Flush(); err != nil {
|
|
||||||
return nil, c.fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.readTimeout != 0 {
|
|
||||||
c.conn.SetReadDeadline(time.Now().Add(c.readTimeout))
|
|
||||||
}
|
|
||||||
|
|
||||||
if cmd == "" {
|
|
||||||
reply := make([]interface{}, pending)
|
|
||||||
for i := range reply {
|
|
||||||
r, e := c.readReply()
|
|
||||||
if e != nil {
|
|
||||||
return nil, c.fatal(e)
|
|
||||||
}
|
|
||||||
reply[i] = r
|
|
||||||
}
|
|
||||||
return reply, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var reply interface{}
|
|
||||||
for i := 0; i <= pending; i++ {
|
|
||||||
var e error
|
|
||||||
if reply, e = c.readReply(); e != nil {
|
|
||||||
return nil, c.fatal(e)
|
|
||||||
}
|
|
||||||
if e, ok := reply.(Error); ok && err == nil {
|
|
||||||
err = e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return reply, err
|
|
||||||
}
|
|
|
@ -1,167 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
// Package redis is a client for the Redis database.
|
|
||||||
//
|
|
||||||
// The Redigo FAQ (https://github.com/garyburd/redigo/wiki/FAQ) contains more
|
|
||||||
// documentation about this package.
|
|
||||||
//
|
|
||||||
// Connections
|
|
||||||
//
|
|
||||||
// The Conn interface is the primary interface for working with Redis.
|
|
||||||
// Applications create connections by calling the Dial, DialWithTimeout or
|
|
||||||
// NewConn functions. In the future, functions will be added for creating
|
|
||||||
// sharded and other types of connections.
|
|
||||||
//
|
|
||||||
// The application must call the connection Close method when the application
|
|
||||||
// is done with the connection.
|
|
||||||
//
|
|
||||||
// Executing Commands
|
|
||||||
//
|
|
||||||
// The Conn interface has a generic method for executing Redis commands:
|
|
||||||
//
|
|
||||||
// Do(commandName string, args ...interface{}) (reply interface{}, err error)
|
|
||||||
//
|
|
||||||
// The Redis command reference (http://redis.io/commands) lists the available
|
|
||||||
// commands. An example of using the Redis APPEND command is:
|
|
||||||
//
|
|
||||||
// n, err := conn.Do("APPEND", "key", "value")
|
|
||||||
//
|
|
||||||
// The Do method converts command arguments to binary strings for transmission
|
|
||||||
// to the server as follows:
|
|
||||||
//
|
|
||||||
// Go Type Conversion
|
|
||||||
// []byte Sent as is
|
|
||||||
// string Sent as is
|
|
||||||
// int, int64 strconv.FormatInt(v)
|
|
||||||
// float64 strconv.FormatFloat(v, 'g', -1, 64)
|
|
||||||
// bool true -> "1", false -> "0"
|
|
||||||
// nil ""
|
|
||||||
// all other types fmt.Print(v)
|
|
||||||
//
|
|
||||||
// Redis command reply types are represented using the following Go types:
|
|
||||||
//
|
|
||||||
// Redis type Go type
|
|
||||||
// error redis.Error
|
|
||||||
// integer int64
|
|
||||||
// simple string string
|
|
||||||
// bulk string []byte or nil if value not present.
|
|
||||||
// array []interface{} or nil if value not present.
|
|
||||||
//
|
|
||||||
// Use type assertions or the reply helper functions to convert from
|
|
||||||
// interface{} to the specific Go type for the command result.
|
|
||||||
//
|
|
||||||
// Pipelining
|
|
||||||
//
|
|
||||||
// Connections support pipelining using the Send, Flush and Receive methods.
|
|
||||||
//
|
|
||||||
// Send(commandName string, args ...interface{}) error
|
|
||||||
// Flush() error
|
|
||||||
// Receive() (reply interface{}, err error)
|
|
||||||
//
|
|
||||||
// Send writes the command to the connection's output buffer. Flush flushes the
|
|
||||||
// connection's output buffer to the server. Receive reads a single reply from
|
|
||||||
// the server. The following example shows a simple pipeline.
|
|
||||||
//
|
|
||||||
// c.Send("SET", "foo", "bar")
|
|
||||||
// c.Send("GET", "foo")
|
|
||||||
// c.Flush()
|
|
||||||
// c.Receive() // reply from SET
|
|
||||||
// v, err = c.Receive() // reply from GET
|
|
||||||
//
|
|
||||||
// The Do method combines the functionality of the Send, Flush and Receive
|
|
||||||
// methods. The Do method starts by writing the command and flushing the output
|
|
||||||
// buffer. Next, the Do method receives all pending replies including the reply
|
|
||||||
// for the command just sent by Do. If any of the received replies is an error,
|
|
||||||
// then Do returns the error. If there are no errors, then Do returns the last
|
|
||||||
// reply. If the command argument to the Do method is "", then the Do method
|
|
||||||
// will flush the output buffer and receive pending replies without sending a
|
|
||||||
// command.
|
|
||||||
//
|
|
||||||
// Use the Send and Do methods to implement pipelined transactions.
|
|
||||||
//
|
|
||||||
// c.Send("MULTI")
|
|
||||||
// c.Send("INCR", "foo")
|
|
||||||
// c.Send("INCR", "bar")
|
|
||||||
// r, err := c.Do("EXEC")
|
|
||||||
// fmt.Println(r) // prints [1, 1]
|
|
||||||
//
|
|
||||||
// Concurrency
|
|
||||||
//
|
|
||||||
// Connections support a single concurrent caller to the write methods (Send,
|
|
||||||
// Flush) and a single concurrent caller to the read method (Receive). Because
|
|
||||||
// Do method combines the functionality of Send, Flush and Receive, the Do
|
|
||||||
// method cannot be called concurrently with the other methods.
|
|
||||||
//
|
|
||||||
// For full concurrent access to Redis, use the thread-safe Pool to get and
|
|
||||||
// release connections from within a goroutine.
|
|
||||||
//
|
|
||||||
// Publish and Subscribe
|
|
||||||
//
|
|
||||||
// Use the Send, Flush and Receive methods to implement Pub/Sub subscribers.
|
|
||||||
//
|
|
||||||
// c.Send("SUBSCRIBE", "example")
|
|
||||||
// c.Flush()
|
|
||||||
// for {
|
|
||||||
// reply, err := c.Receive()
|
|
||||||
// if err != nil {
|
|
||||||
// return err
|
|
||||||
// }
|
|
||||||
// // process pushed message
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The PubSubConn type wraps a Conn with convenience methods for implementing
|
|
||||||
// subscribers. The Subscribe, PSubscribe, Unsubscribe and PUnsubscribe methods
|
|
||||||
// send and flush a subscription management command. The receive method
|
|
||||||
// converts a pushed message to convenient types for use in a type switch.
|
|
||||||
//
|
|
||||||
// psc := PubSubConn{c}
|
|
||||||
// psc.Subscribe("example")
|
|
||||||
// for {
|
|
||||||
// switch v := psc.Receive().(type) {
|
|
||||||
// case redis.Message:
|
|
||||||
// fmt.Printf("%s: message: %s\n", v.Channel, v.Data)
|
|
||||||
// case redis.Subscription:
|
|
||||||
// fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
|
|
||||||
// case error:
|
|
||||||
// return v
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// Reply Helpers
|
|
||||||
//
|
|
||||||
// The Bool, Int, Bytes, String, Strings and Values functions convert a reply
|
|
||||||
// to a value of a specific type. To allow convenient wrapping of calls to the
|
|
||||||
// connection Do and Receive methods, the functions take a second argument of
|
|
||||||
// type error. If the error is non-nil, then the helper function returns the
|
|
||||||
// error. If the error is nil, the function converts the reply to the specified
|
|
||||||
// type:
|
|
||||||
//
|
|
||||||
// exists, err := redis.Bool(c.Do("EXISTS", "foo"))
|
|
||||||
// if err != nil {
|
|
||||||
// // handle error return from c.Do or type conversion error.
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// The Scan function converts elements of a array reply to Go types:
|
|
||||||
//
|
|
||||||
// var value1 int
|
|
||||||
// var value2 string
|
|
||||||
// reply, err := redis.Values(c.Do("MGET", "key1", "key2"))
|
|
||||||
// if err != nil {
|
|
||||||
// // handle error
|
|
||||||
// }
|
|
||||||
// if _, err := redis.Scan(reply, &value1, &value2); err != nil {
|
|
||||||
// // handle error
|
|
||||||
// }
|
|
||||||
package redis
|
|
|
@ -1,117 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewLoggingConn returns a logging wrapper around a connection.
|
|
||||||
func NewLoggingConn(conn Conn, logger *log.Logger, prefix string) Conn {
|
|
||||||
if prefix != "" {
|
|
||||||
prefix = prefix + "."
|
|
||||||
}
|
|
||||||
return &loggingConn{conn, logger, prefix}
|
|
||||||
}
|
|
||||||
|
|
||||||
type loggingConn struct {
|
|
||||||
Conn
|
|
||||||
logger *log.Logger
|
|
||||||
prefix string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Close() error {
|
|
||||||
err := c.Conn.Close()
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprintf(&buf, "%sClose() -> (%v)", c.prefix, err)
|
|
||||||
c.logger.Output(2, buf.String())
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) printValue(buf *bytes.Buffer, v interface{}) {
|
|
||||||
const chop = 32
|
|
||||||
switch v := v.(type) {
|
|
||||||
case []byte:
|
|
||||||
if len(v) > chop {
|
|
||||||
fmt.Fprintf(buf, "%q...", v[:chop])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%q", v)
|
|
||||||
}
|
|
||||||
case string:
|
|
||||||
if len(v) > chop {
|
|
||||||
fmt.Fprintf(buf, "%q...", v[:chop])
|
|
||||||
} else {
|
|
||||||
fmt.Fprintf(buf, "%q", v)
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
if len(v) == 0 {
|
|
||||||
buf.WriteString("[]")
|
|
||||||
} else {
|
|
||||||
sep := "["
|
|
||||||
fin := "]"
|
|
||||||
if len(v) > chop {
|
|
||||||
v = v[:chop]
|
|
||||||
fin = "...]"
|
|
||||||
}
|
|
||||||
for _, vv := range v {
|
|
||||||
buf.WriteString(sep)
|
|
||||||
c.printValue(buf, vv)
|
|
||||||
sep = ", "
|
|
||||||
}
|
|
||||||
buf.WriteString(fin)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Fprint(buf, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) print(method, commandName string, args []interface{}, reply interface{}, err error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
fmt.Fprintf(&buf, "%s%s(", c.prefix, method)
|
|
||||||
if method != "Receive" {
|
|
||||||
buf.WriteString(commandName)
|
|
||||||
for _, arg := range args {
|
|
||||||
buf.WriteString(", ")
|
|
||||||
c.printValue(&buf, arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString(") -> (")
|
|
||||||
if method != "Send" {
|
|
||||||
c.printValue(&buf, reply)
|
|
||||||
buf.WriteString(", ")
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&buf, "%v)", err)
|
|
||||||
c.logger.Output(3, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Do(commandName string, args ...interface{}) (interface{}, error) {
|
|
||||||
reply, err := c.Conn.Do(commandName, args...)
|
|
||||||
c.print("Do", commandName, args, reply, err)
|
|
||||||
return reply, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Send(commandName string, args ...interface{}) error {
|
|
||||||
err := c.Conn.Send(commandName, args...)
|
|
||||||
c.print("Send", commandName, args, nil, err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *loggingConn) Receive() (interface{}, error) {
|
|
||||||
reply, err := c.Conn.Receive()
|
|
||||||
c.print("Receive", "", nil, reply, err)
|
|
||||||
return reply, err
|
|
||||||
}
|
|
|
@ -1,358 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"container/list"
|
|
||||||
"crypto/rand"
|
|
||||||
"crypto/sha1"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"strconv"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var nowFunc = time.Now // for testing
|
|
||||||
|
|
||||||
// ErrPoolExhausted is returned from a pool connection method (Do, Send,
|
|
||||||
// Receive, Flush, Err) when the maximum number of database connections in the
|
|
||||||
// pool has been reached.
|
|
||||||
var ErrPoolExhausted = errors.New("redigo: connection pool exhausted")
|
|
||||||
|
|
||||||
var errPoolClosed = errors.New("redigo: connection pool closed")
|
|
||||||
|
|
||||||
// Pool maintains a pool of connections. The application calls the Get method
|
|
||||||
// to get a connection from the pool and the connection's Close method to
|
|
||||||
// return the connection's resources to the pool.
|
|
||||||
//
|
|
||||||
// The following example shows how to use a pool in a web application. The
|
|
||||||
// application creates a pool at application startup and makes it available to
|
|
||||||
// request handlers using a global variable.
|
|
||||||
//
|
|
||||||
// func newPool(server, password string) *redis.Pool {
|
|
||||||
// return &redis.Pool{
|
|
||||||
// MaxIdle: 3,
|
|
||||||
// IdleTimeout: 240 * time.Second,
|
|
||||||
// Dial: func () (redis.Conn, error) {
|
|
||||||
// c, err := redis.Dial("tcp", server)
|
|
||||||
// if err != nil {
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// if _, err := c.Do("AUTH", password); err != nil {
|
|
||||||
// c.Close()
|
|
||||||
// return nil, err
|
|
||||||
// }
|
|
||||||
// return c, err
|
|
||||||
// },
|
|
||||||
// TestOnBorrow: func(c redis.Conn, t time.Time) error {
|
|
||||||
// _, err := c.Do("PING")
|
|
||||||
// return err
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var (
|
|
||||||
// pool *redis.Pool
|
|
||||||
// redisServer = flag.String("redisServer", ":6379", "")
|
|
||||||
// redisPassword = flag.String("redisPassword", "", "")
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// func main() {
|
|
||||||
// flag.Parse()
|
|
||||||
// pool = newPool(*redisServer, *redisPassword)
|
|
||||||
// ...
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// A request handler gets a connection from the pool and closes the connection
|
|
||||||
// when the handler is done:
|
|
||||||
//
|
|
||||||
// func serveHome(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// conn := pool.Get()
|
|
||||||
// defer conn.Close()
|
|
||||||
// ....
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
type Pool struct {
|
|
||||||
|
|
||||||
// Dial is an application supplied function for creating new connections.
|
|
||||||
Dial func() (Conn, error)
|
|
||||||
|
|
||||||
// TestOnBorrow is an optional application supplied function for checking
|
|
||||||
// the health of an idle connection before the connection is used again by
|
|
||||||
// the application. Argument t is the time that the connection was returned
|
|
||||||
// to the pool. If the function returns an error, then the connection is
|
|
||||||
// closed.
|
|
||||||
TestOnBorrow func(c Conn, t time.Time) error
|
|
||||||
|
|
||||||
// Maximum number of idle connections in the pool.
|
|
||||||
MaxIdle int
|
|
||||||
|
|
||||||
// Maximum number of connections allocated by the pool at a given time.
|
|
||||||
// When zero, there is no limit on the number of connections in the pool.
|
|
||||||
MaxActive int
|
|
||||||
|
|
||||||
// Close connections after remaining idle for this duration. If the value
|
|
||||||
// is zero, then idle connections are not closed. Applications should set
|
|
||||||
// the timeout to a value less than the server's timeout.
|
|
||||||
IdleTimeout time.Duration
|
|
||||||
|
|
||||||
// mu protects fields defined below.
|
|
||||||
mu sync.Mutex
|
|
||||||
closed bool
|
|
||||||
active int
|
|
||||||
|
|
||||||
// Stack of idleConn with most recently used at the front.
|
|
||||||
idle list.List
|
|
||||||
}
|
|
||||||
|
|
||||||
type idleConn struct {
|
|
||||||
c Conn
|
|
||||||
t time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPool is a convenience function for initializing a pool.
|
|
||||||
func NewPool(newFn func() (Conn, error), maxIdle int) *Pool {
|
|
||||||
return &Pool{Dial: newFn, MaxIdle: maxIdle}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get gets a connection. The application must close the returned connection.
|
|
||||||
// The connection acquires an underlying connection on the first call to the
|
|
||||||
// connection Do, Send, Receive, Flush or Err methods. An application can force
|
|
||||||
// the connection to acquire an underlying connection without executing a Redis
|
|
||||||
// command by calling the Err method.
|
|
||||||
func (p *Pool) Get() Conn {
|
|
||||||
return &pooledConnection{p: p}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActiveCount returns the number of active connections in the pool.
|
|
||||||
func (p *Pool) ActiveCount() int {
|
|
||||||
p.mu.Lock()
|
|
||||||
active := p.active
|
|
||||||
p.mu.Unlock()
|
|
||||||
return active
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close releases the resources used by the pool.
|
|
||||||
func (p *Pool) Close() error {
|
|
||||||
p.mu.Lock()
|
|
||||||
idle := p.idle
|
|
||||||
p.idle.Init()
|
|
||||||
p.closed = true
|
|
||||||
p.active -= idle.Len()
|
|
||||||
p.mu.Unlock()
|
|
||||||
for e := idle.Front(); e != nil; e = e.Next() {
|
|
||||||
e.Value.(idleConn).c.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// get prunes stale connections and returns a connection from the idle list or
|
|
||||||
// creates a new connection.
|
|
||||||
func (p *Pool) get() (Conn, error) {
|
|
||||||
p.mu.Lock()
|
|
||||||
|
|
||||||
if p.closed {
|
|
||||||
p.mu.Unlock()
|
|
||||||
return nil, errors.New("redigo: get on closed pool")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune stale connections.
|
|
||||||
|
|
||||||
if timeout := p.IdleTimeout; timeout > 0 {
|
|
||||||
for i, n := 0, p.idle.Len(); i < n; i++ {
|
|
||||||
e := p.idle.Back()
|
|
||||||
if e == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ic := e.Value.(idleConn)
|
|
||||||
if ic.t.Add(timeout).After(nowFunc()) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
p.idle.Remove(e)
|
|
||||||
p.active -= 1
|
|
||||||
p.mu.Unlock()
|
|
||||||
ic.c.Close()
|
|
||||||
p.mu.Lock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get idle connection.
|
|
||||||
|
|
||||||
for i, n := 0, p.idle.Len(); i < n; i++ {
|
|
||||||
e := p.idle.Front()
|
|
||||||
if e == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
ic := e.Value.(idleConn)
|
|
||||||
p.idle.Remove(e)
|
|
||||||
test := p.TestOnBorrow
|
|
||||||
p.mu.Unlock()
|
|
||||||
if test == nil || test(ic.c, ic.t) == nil {
|
|
||||||
return ic.c, nil
|
|
||||||
}
|
|
||||||
ic.c.Close()
|
|
||||||
p.mu.Lock()
|
|
||||||
p.active -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.MaxActive > 0 && p.active >= p.MaxActive {
|
|
||||||
p.mu.Unlock()
|
|
||||||
return nil, ErrPoolExhausted
|
|
||||||
}
|
|
||||||
|
|
||||||
// No idle connection, create new.
|
|
||||||
|
|
||||||
dial := p.Dial
|
|
||||||
p.active += 1
|
|
||||||
p.mu.Unlock()
|
|
||||||
c, err := dial()
|
|
||||||
if err != nil {
|
|
||||||
p.mu.Lock()
|
|
||||||
p.active -= 1
|
|
||||||
p.mu.Unlock()
|
|
||||||
c = nil
|
|
||||||
}
|
|
||||||
return c, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Pool) put(c Conn, forceClose bool) error {
|
|
||||||
if c.Err() == nil && !forceClose {
|
|
||||||
p.mu.Lock()
|
|
||||||
if !p.closed {
|
|
||||||
p.idle.PushFront(idleConn{t: nowFunc(), c: c})
|
|
||||||
if p.idle.Len() > p.MaxIdle {
|
|
||||||
c = p.idle.Remove(p.idle.Back()).(idleConn).c
|
|
||||||
} else {
|
|
||||||
c = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
p.mu.Unlock()
|
|
||||||
}
|
|
||||||
if c != nil {
|
|
||||||
p.mu.Lock()
|
|
||||||
p.active -= 1
|
|
||||||
p.mu.Unlock()
|
|
||||||
return c.Close()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type pooledConnection struct {
|
|
||||||
c Conn
|
|
||||||
err error
|
|
||||||
p *Pool
|
|
||||||
state int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) get() error {
|
|
||||||
if c.err == nil && c.c == nil {
|
|
||||||
c.c, c.err = c.p.get()
|
|
||||||
}
|
|
||||||
return c.err
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
sentinel []byte
|
|
||||||
sentinelOnce sync.Once
|
|
||||||
)
|
|
||||||
|
|
||||||
func initSentinel() {
|
|
||||||
p := make([]byte, 64)
|
|
||||||
if _, err := rand.Read(p); err == nil {
|
|
||||||
sentinel = p
|
|
||||||
} else {
|
|
||||||
h := sha1.New()
|
|
||||||
io.WriteString(h, "Oops, rand failed. Use time instead.")
|
|
||||||
io.WriteString(h, strconv.FormatInt(time.Now().UnixNano(), 10))
|
|
||||||
sentinel = h.Sum(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Close() (err error) {
|
|
||||||
if c.c != nil {
|
|
||||||
if c.state&multiState != 0 {
|
|
||||||
c.c.Send("DISCARD")
|
|
||||||
c.state &^= (multiState | watchState)
|
|
||||||
} else if c.state&watchState != 0 {
|
|
||||||
c.c.Send("UNWATCH")
|
|
||||||
c.state &^= watchState
|
|
||||||
}
|
|
||||||
if c.state&subscribeState != 0 {
|
|
||||||
c.c.Send("UNSUBSCRIBE")
|
|
||||||
c.c.Send("PUNSUBSCRIBE")
|
|
||||||
// To detect the end of the message stream, ask the server to echo
|
|
||||||
// a sentinel value and read until we see that value.
|
|
||||||
sentinelOnce.Do(initSentinel)
|
|
||||||
c.c.Send("ECHO", sentinel)
|
|
||||||
c.c.Flush()
|
|
||||||
for {
|
|
||||||
p, err := c.c.Receive()
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if p, ok := p.([]byte); ok && bytes.Equal(p, sentinel) {
|
|
||||||
c.state &^= subscribeState
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.c.Do("")
|
|
||||||
c.p.put(c.c, c.state != 0)
|
|
||||||
c.c = nil
|
|
||||||
c.err = errPoolClosed
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Err() error {
|
|
||||||
if err := c.get(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.c.Err()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
|
|
||||||
if err := c.get(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ci := lookupCommandInfo(commandName)
|
|
||||||
c.state = (c.state | ci.set) &^ ci.clear
|
|
||||||
return c.c.Do(commandName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Send(commandName string, args ...interface{}) error {
|
|
||||||
if err := c.get(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ci := lookupCommandInfo(commandName)
|
|
||||||
c.state = (c.state | ci.set) &^ ci.clear
|
|
||||||
return c.c.Send(commandName, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Flush() error {
|
|
||||||
if err := c.get(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return c.c.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *pooledConnection) Receive() (reply interface{}, err error) {
|
|
||||||
if err := c.get(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return c.c.Receive()
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Subscription represents a subscribe or unsubscribe notification.
|
|
||||||
type Subscription struct {
|
|
||||||
|
|
||||||
// Kind is "subscribe", "unsubscribe", "psubscribe" or "punsubscribe"
|
|
||||||
Kind string
|
|
||||||
|
|
||||||
// The channel that was changed.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The current number of subscriptions for connection.
|
|
||||||
Count int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message represents a message notification.
|
|
||||||
type Message struct {
|
|
||||||
|
|
||||||
// The originating channel.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The message data.
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// PMessage represents a pmessage notification.
|
|
||||||
type PMessage struct {
|
|
||||||
|
|
||||||
// The matched pattern.
|
|
||||||
Pattern string
|
|
||||||
|
|
||||||
// The originating channel.
|
|
||||||
Channel string
|
|
||||||
|
|
||||||
// The message data.
|
|
||||||
Data []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// PubSubConn wraps a Conn with convenience methods for subscribers.
|
|
||||||
type PubSubConn struct {
|
|
||||||
Conn Conn
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes the connection.
|
|
||||||
func (c PubSubConn) Close() error {
|
|
||||||
return c.Conn.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subscribe subscribes the connection to the specified channels.
|
|
||||||
func (c PubSubConn) Subscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("SUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PSubscribe subscribes the connection to the given patterns.
|
|
||||||
func (c PubSubConn) PSubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("PSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unsubscribe unsubscribes the connection from the given channels, or from all
|
|
||||||
// of them if none is given.
|
|
||||||
func (c PubSubConn) Unsubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("UNSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// PUnsubscribe unsubscribes the connection from the given patterns, or from all
|
|
||||||
// of them if none is given.
|
|
||||||
func (c PubSubConn) PUnsubscribe(channel ...interface{}) error {
|
|
||||||
c.Conn.Send("PUNSUBSCRIBE", channel...)
|
|
||||||
return c.Conn.Flush()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive returns a pushed message as a Subscription, Message, PMessage or
|
|
||||||
// error. The return value is intended to be used directly in a type switch as
|
|
||||||
// illustrated in the PubSubConn example.
|
|
||||||
func (c PubSubConn) Receive() interface{} {
|
|
||||||
reply, err := Values(c.Conn.Receive())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var kind string
|
|
||||||
reply, err = Scan(reply, &kind)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
switch kind {
|
|
||||||
case "message":
|
|
||||||
var m Message
|
|
||||||
if _, err := Scan(reply, &m.Channel, &m.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
case "pmessage":
|
|
||||||
var pm PMessage
|
|
||||||
if _, err := Scan(reply, &pm.Pattern, &pm.Channel, &pm.Data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return pm
|
|
||||||
case "subscribe", "psubscribe", "unsubscribe", "punsubscribe":
|
|
||||||
s := Subscription{Kind: kind}
|
|
||||||
if _, err := Scan(reply, &s.Channel, &s.Count); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
return errors.New("redigo: unknown pubsub notification")
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
// Error represents an error returned in a command reply.
|
|
||||||
type Error string
|
|
||||||
|
|
||||||
func (err Error) Error() string { return string(err) }
|
|
||||||
|
|
||||||
// Conn represents a connection to a Redis server.
|
|
||||||
type Conn interface {
|
|
||||||
// Close closes the connection.
|
|
||||||
Close() error
|
|
||||||
|
|
||||||
// Err returns a non-nil value if the connection is broken. The returned
|
|
||||||
// value is either the first non-nil value returned from the underlying
|
|
||||||
// network connection or a protocol parsing error. Applications should
|
|
||||||
// close broken connections.
|
|
||||||
Err() error
|
|
||||||
|
|
||||||
// Do sends a command to the server and returns the received reply.
|
|
||||||
Do(commandName string, args ...interface{}) (reply interface{}, err error)
|
|
||||||
|
|
||||||
// Send writes the command to the client's output buffer.
|
|
||||||
Send(commandName string, args ...interface{}) error
|
|
||||||
|
|
||||||
// Flush flushes the output buffer to the Redis server.
|
|
||||||
Flush() error
|
|
||||||
|
|
||||||
// Receive receives a single reply from the Redis server
|
|
||||||
Receive() (reply interface{}, err error)
|
|
||||||
}
|
|
|
@ -1,513 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ensureLen(d reflect.Value, n int) {
|
|
||||||
if n > d.Cap() {
|
|
||||||
d.Set(reflect.MakeSlice(d.Type(), n, n))
|
|
||||||
} else {
|
|
||||||
d.SetLen(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cannotConvert(d reflect.Value, s interface{}) error {
|
|
||||||
return fmt.Errorf("redigo: Scan cannot convert from %s to %s",
|
|
||||||
reflect.TypeOf(s), d.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignBytes(d reflect.Value, s []byte) (err error) {
|
|
||||||
switch d.Type().Kind() {
|
|
||||||
case reflect.Float32, reflect.Float64:
|
|
||||||
var x float64
|
|
||||||
x, err = strconv.ParseFloat(string(s), d.Type().Bits())
|
|
||||||
d.SetFloat(x)
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
var x int64
|
|
||||||
x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
|
|
||||||
d.SetInt(x)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
var x uint64
|
|
||||||
x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
|
|
||||||
d.SetUint(x)
|
|
||||||
case reflect.Bool:
|
|
||||||
var x bool
|
|
||||||
x, err = strconv.ParseBool(string(s))
|
|
||||||
d.SetBool(x)
|
|
||||||
case reflect.String:
|
|
||||||
d.SetString(string(s))
|
|
||||||
case reflect.Slice:
|
|
||||||
if d.Type().Elem().Kind() != reflect.Uint8 {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
d.SetBytes(s)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignInt(d reflect.Value, s int64) (err error) {
|
|
||||||
switch d.Type().Kind() {
|
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
|
||||||
d.SetInt(s)
|
|
||||||
if d.Int() != s {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
d.SetInt(0)
|
|
||||||
}
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
|
||||||
if s < 0 {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
} else {
|
|
||||||
x := uint64(s)
|
|
||||||
d.SetUint(x)
|
|
||||||
if d.Uint() != x {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
d.SetUint(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Bool:
|
|
||||||
d.SetBool(s != 0)
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignValue(d reflect.Value, s interface{}) (err error) {
|
|
||||||
switch s := s.(type) {
|
|
||||||
case []byte:
|
|
||||||
err = convertAssignBytes(d, s)
|
|
||||||
case int64:
|
|
||||||
err = convertAssignInt(d, s)
|
|
||||||
default:
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssignValues(d reflect.Value, s []interface{}) error {
|
|
||||||
if d.Type().Kind() != reflect.Slice {
|
|
||||||
return cannotConvert(d, s)
|
|
||||||
}
|
|
||||||
ensureLen(d, len(s))
|
|
||||||
for i := 0; i < len(s); i++ {
|
|
||||||
if err := convertAssignValue(d.Index(i), s[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertAssign(d interface{}, s interface{}) (err error) {
|
|
||||||
// Handle the most common destination types using type switches and
|
|
||||||
// fall back to reflection for all other types.
|
|
||||||
switch s := s.(type) {
|
|
||||||
case nil:
|
|
||||||
// ingore
|
|
||||||
case []byte:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *string:
|
|
||||||
*d = string(s)
|
|
||||||
case *int:
|
|
||||||
*d, err = strconv.Atoi(string(s))
|
|
||||||
case *bool:
|
|
||||||
*d, err = strconv.ParseBool(string(s))
|
|
||||||
case *[]byte:
|
|
||||||
*d = s
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignBytes(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case int64:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *int:
|
|
||||||
x := int(s)
|
|
||||||
if int64(x) != s {
|
|
||||||
err = strconv.ErrRange
|
|
||||||
x = 0
|
|
||||||
}
|
|
||||||
*d = x
|
|
||||||
case *bool:
|
|
||||||
*d = s != 0
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignInt(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case []interface{}:
|
|
||||||
switch d := d.(type) {
|
|
||||||
case *[]interface{}:
|
|
||||||
*d = s
|
|
||||||
case *interface{}:
|
|
||||||
*d = s
|
|
||||||
case nil:
|
|
||||||
// skip value
|
|
||||||
default:
|
|
||||||
if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
|
|
||||||
err = cannotConvert(d, s)
|
|
||||||
} else {
|
|
||||||
err = convertAssignValues(d.Elem(), s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Error:
|
|
||||||
err = s
|
|
||||||
default:
|
|
||||||
err = cannotConvert(reflect.ValueOf(d), s)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan copies from src to the values pointed at by dest.
|
|
||||||
//
|
|
||||||
// The values pointed at by dest must be an integer, float, boolean, string,
|
|
||||||
// []byte, interface{} or slices of these types. Scan uses the standard strconv
|
|
||||||
// package to convert bulk strings to numeric and boolean types.
|
|
||||||
//
|
|
||||||
// If a dest value is nil, then the corresponding src value is skipped.
|
|
||||||
//
|
|
||||||
// If a src element is nil, then the corresponding dest value is not modified.
|
|
||||||
//
|
|
||||||
// To enable easy use of Scan in a loop, Scan returns the slice of src
|
|
||||||
// following the copied values.
|
|
||||||
func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) {
|
|
||||||
if len(src) < len(dest) {
|
|
||||||
return nil, errors.New("redigo: Scan array short")
|
|
||||||
}
|
|
||||||
var err error
|
|
||||||
for i, d := range dest {
|
|
||||||
err = convertAssign(d, src[i])
|
|
||||||
if err != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return src[len(dest):], err
|
|
||||||
}
|
|
||||||
|
|
||||||
type fieldSpec struct {
|
|
||||||
name string
|
|
||||||
index []int
|
|
||||||
//omitEmpty bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type structSpec struct {
|
|
||||||
m map[string]*fieldSpec
|
|
||||||
l []*fieldSpec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ss *structSpec) fieldSpec(name []byte) *fieldSpec {
|
|
||||||
return ss.m[string(name)]
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) {
|
|
||||||
for i := 0; i < t.NumField(); i++ {
|
|
||||||
f := t.Field(i)
|
|
||||||
switch {
|
|
||||||
case f.PkgPath != "":
|
|
||||||
// Ignore unexported fields.
|
|
||||||
case f.Anonymous:
|
|
||||||
// TODO: Handle pointers. Requires change to decoder and
|
|
||||||
// protection against infinite recursion.
|
|
||||||
if f.Type.Kind() == reflect.Struct {
|
|
||||||
compileStructSpec(f.Type, depth, append(index, i), ss)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fs := &fieldSpec{name: f.Name}
|
|
||||||
tag := f.Tag.Get("redis")
|
|
||||||
p := strings.Split(tag, ",")
|
|
||||||
if len(p) > 0 {
|
|
||||||
if p[0] == "-" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if len(p[0]) > 0 {
|
|
||||||
fs.name = p[0]
|
|
||||||
}
|
|
||||||
for _, s := range p[1:] {
|
|
||||||
switch s {
|
|
||||||
//case "omitempty":
|
|
||||||
// fs.omitempty = true
|
|
||||||
default:
|
|
||||||
panic(errors.New("redigo: unknown field flag " + s + " for type " + t.Name()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d, found := depth[fs.name]
|
|
||||||
if !found {
|
|
||||||
d = 1 << 30
|
|
||||||
}
|
|
||||||
switch {
|
|
||||||
case len(index) == d:
|
|
||||||
// At same depth, remove from result.
|
|
||||||
delete(ss.m, fs.name)
|
|
||||||
j := 0
|
|
||||||
for i := 0; i < len(ss.l); i++ {
|
|
||||||
if fs.name != ss.l[i].name {
|
|
||||||
ss.l[j] = ss.l[i]
|
|
||||||
j += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ss.l = ss.l[:j]
|
|
||||||
case len(index) < d:
|
|
||||||
fs.index = make([]int, len(index)+1)
|
|
||||||
copy(fs.index, index)
|
|
||||||
fs.index[len(index)] = i
|
|
||||||
depth[fs.name] = len(index)
|
|
||||||
ss.m[fs.name] = fs
|
|
||||||
ss.l = append(ss.l, fs)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
structSpecMutex sync.RWMutex
|
|
||||||
structSpecCache = make(map[reflect.Type]*structSpec)
|
|
||||||
defaultFieldSpec = &fieldSpec{}
|
|
||||||
)
|
|
||||||
|
|
||||||
func structSpecForType(t reflect.Type) *structSpec {
|
|
||||||
|
|
||||||
structSpecMutex.RLock()
|
|
||||||
ss, found := structSpecCache[t]
|
|
||||||
structSpecMutex.RUnlock()
|
|
||||||
if found {
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
structSpecMutex.Lock()
|
|
||||||
defer structSpecMutex.Unlock()
|
|
||||||
ss, found = structSpecCache[t]
|
|
||||||
if found {
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
ss = &structSpec{m: make(map[string]*fieldSpec)}
|
|
||||||
compileStructSpec(t, make(map[string]int), nil, ss)
|
|
||||||
structSpecCache[t] = ss
|
|
||||||
return ss
|
|
||||||
}
|
|
||||||
|
|
||||||
var errScanStructValue = errors.New("redigo: ScanStruct value must be non-nil pointer to a struct")
|
|
||||||
|
|
||||||
// ScanStruct scans alternating names and values from src to a struct. The
|
|
||||||
// HGETALL and CONFIG GET commands return replies in this format.
|
|
||||||
//
|
|
||||||
// ScanStruct uses exported field names to match values in the response. Use
|
|
||||||
// 'redis' field tag to override the name:
|
|
||||||
//
|
|
||||||
// Field int `redis:"myName"`
|
|
||||||
//
|
|
||||||
// Fields with the tag redis:"-" are ignored.
|
|
||||||
//
|
|
||||||
// Integer, float, boolean, string and []byte fields are supported. Scan uses the
|
|
||||||
// standard strconv package to convert bulk string values to numeric and
|
|
||||||
// boolean types.
|
|
||||||
//
|
|
||||||
// If a src element is nil, then the corresponding field is not modified.
|
|
||||||
func ScanStruct(src []interface{}, dest interface{}) error {
|
|
||||||
d := reflect.ValueOf(dest)
|
|
||||||
if d.Kind() != reflect.Ptr || d.IsNil() {
|
|
||||||
return errScanStructValue
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
if d.Kind() != reflect.Struct {
|
|
||||||
return errScanStructValue
|
|
||||||
}
|
|
||||||
ss := structSpecForType(d.Type())
|
|
||||||
|
|
||||||
if len(src)%2 != 0 {
|
|
||||||
return errors.New("redigo: ScanStruct expects even number of values in values")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(src); i += 2 {
|
|
||||||
s := src[i+1]
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name, ok := src[i].([]byte)
|
|
||||||
if !ok {
|
|
||||||
return errors.New("redigo: ScanStruct key not a bulk string value")
|
|
||||||
}
|
|
||||||
fs := ss.fieldSpec(name)
|
|
||||||
if fs == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
errScanSliceValue = errors.New("redigo: ScanSlice dest must be non-nil pointer to a struct")
|
|
||||||
)
|
|
||||||
|
|
||||||
// ScanSlice scans src to the slice pointed to by dest. The elements the dest
|
|
||||||
// slice must be integer, float, boolean, string, struct or pointer to struct
|
|
||||||
// values.
|
|
||||||
//
|
|
||||||
// Struct fields must be integer, float, boolean or string values. All struct
|
|
||||||
// fields are used unless a subset is specified using fieldNames.
|
|
||||||
func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error {
|
|
||||||
d := reflect.ValueOf(dest)
|
|
||||||
if d.Kind() != reflect.Ptr || d.IsNil() {
|
|
||||||
return errScanSliceValue
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
if d.Kind() != reflect.Slice {
|
|
||||||
return errScanSliceValue
|
|
||||||
}
|
|
||||||
|
|
||||||
isPtr := false
|
|
||||||
t := d.Type().Elem()
|
|
||||||
if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct {
|
|
||||||
isPtr = true
|
|
||||||
t = t.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
if t.Kind() != reflect.Struct {
|
|
||||||
ensureLen(d, len(src))
|
|
||||||
for i, s := range src {
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.Index(i), s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ss := structSpecForType(t)
|
|
||||||
fss := ss.l
|
|
||||||
if len(fieldNames) > 0 {
|
|
||||||
fss = make([]*fieldSpec, len(fieldNames))
|
|
||||||
for i, name := range fieldNames {
|
|
||||||
fss[i] = ss.m[name]
|
|
||||||
if fss[i] == nil {
|
|
||||||
return errors.New("redigo: ScanSlice bad field name " + name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fss) == 0 {
|
|
||||||
return errors.New("redigo: ScanSlice no struct fields")
|
|
||||||
}
|
|
||||||
|
|
||||||
n := len(src) / len(fss)
|
|
||||||
if n*len(fss) != len(src) {
|
|
||||||
return errors.New("redigo: ScanSlice length not a multiple of struct field count")
|
|
||||||
}
|
|
||||||
|
|
||||||
ensureLen(d, n)
|
|
||||||
for i := 0; i < n; i++ {
|
|
||||||
d := d.Index(i)
|
|
||||||
if isPtr {
|
|
||||||
if d.IsNil() {
|
|
||||||
d.Set(reflect.New(t))
|
|
||||||
}
|
|
||||||
d = d.Elem()
|
|
||||||
}
|
|
||||||
for j, fs := range fss {
|
|
||||||
s := src[i*len(fss)+j]
|
|
||||||
if s == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Args is a helper for constructing command arguments from structured values.
|
|
||||||
type Args []interface{}
|
|
||||||
|
|
||||||
// Add returns the result of appending value to args.
|
|
||||||
func (args Args) Add(value ...interface{}) Args {
|
|
||||||
return append(args, value...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddFlat returns the result of appending the flattened value of v to args.
|
|
||||||
//
|
|
||||||
// Maps are flattened by appending the alternating keys and map values to args.
|
|
||||||
//
|
|
||||||
// Slices are flattened by appending the slice elements to args.
|
|
||||||
//
|
|
||||||
// Structs are flattened by appending the alternating names and values of
|
|
||||||
// exported fields to args. If v is a nil struct pointer, then nothing is
|
|
||||||
// appended. The 'redis' field tag overrides struct field names. See ScanStruct
|
|
||||||
// for more information on the use of the 'redis' field tag.
|
|
||||||
//
|
|
||||||
// Other types are appended to args as is.
|
|
||||||
func (args Args) AddFlat(v interface{}) Args {
|
|
||||||
rv := reflect.ValueOf(v)
|
|
||||||
switch rv.Kind() {
|
|
||||||
case reflect.Struct:
|
|
||||||
args = flattenStruct(args, rv)
|
|
||||||
case reflect.Slice:
|
|
||||||
for i := 0; i < rv.Len(); i++ {
|
|
||||||
args = append(args, rv.Index(i).Interface())
|
|
||||||
}
|
|
||||||
case reflect.Map:
|
|
||||||
for _, k := range rv.MapKeys() {
|
|
||||||
args = append(args, k.Interface(), rv.MapIndex(k).Interface())
|
|
||||||
}
|
|
||||||
case reflect.Ptr:
|
|
||||||
if rv.Type().Elem().Kind() == reflect.Struct {
|
|
||||||
if !rv.IsNil() {
|
|
||||||
args = flattenStruct(args, rv.Elem())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
args = append(args, v)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
func flattenStruct(args Args, v reflect.Value) Args {
|
|
||||||
ss := structSpecForType(v.Type())
|
|
||||||
for _, fs := range ss.l {
|
|
||||||
fv := v.FieldByIndex(fs.index)
|
|
||||||
args = append(args, fs.name, fv.Interface())
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2012 Gary Burd
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
|
||||||
// not use this file except in compliance with the License. You may obtain
|
|
||||||
// a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
// License for the specific language governing permissions and limitations
|
|
||||||
// under the License.
|
|
||||||
|
|
||||||
package redis
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/sha1"
|
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Script encapsulates the source, hash and key count for a Lua script. See
|
|
||||||
// http://redis.io/commands/eval for information on scripts in Redis.
|
|
||||||
type Script struct {
|
|
||||||
keyCount int
|
|
||||||
src string
|
|
||||||
hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewScript returns a new script object. If keyCount is greater than or equal
|
|
||||||
// to zero, then the count is automatically inserted in the EVAL command
|
|
||||||
// argument list. If keyCount is less than zero, then the application supplies
|
|
||||||
// the count as the first value in the keysAndArgs argument to the Do, Send and
|
|
||||||
// SendHash methods.
|
|
||||||
func NewScript(keyCount int, src string) *Script {
|
|
||||||
h := sha1.New()
|
|
||||||
io.WriteString(h, src)
|
|
||||||
return &Script{keyCount, src, hex.EncodeToString(h.Sum(nil))}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Script) args(spec string, keysAndArgs []interface{}) []interface{} {
|
|
||||||
var args []interface{}
|
|
||||||
if s.keyCount < 0 {
|
|
||||||
args = make([]interface{}, 1+len(keysAndArgs))
|
|
||||||
args[0] = spec
|
|
||||||
copy(args[1:], keysAndArgs)
|
|
||||||
} else {
|
|
||||||
args = make([]interface{}, 2+len(keysAndArgs))
|
|
||||||
args[0] = spec
|
|
||||||
args[1] = s.keyCount
|
|
||||||
copy(args[2:], keysAndArgs)
|
|
||||||
}
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do evalutes the script. Under the covers, Do optimistically evaluates the
|
|
||||||
// script using the EVALSHA command. If the command fails because the script is
|
|
||||||
// not loaded, then Do evaluates the script using the EVAL command (thus
|
|
||||||
// causing the script to load).
|
|
||||||
func (s *Script) Do(c Conn, keysAndArgs ...interface{}) (interface{}, error) {
|
|
||||||
v, err := c.Do("EVALSHA", s.args(s.hash, keysAndArgs)...)
|
|
||||||
if e, ok := err.(Error); ok && strings.HasPrefix(string(e), "NOSCRIPT ") {
|
|
||||||
v, err = c.Do("EVAL", s.args(s.src, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
return v, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// SendHash evaluates the script without waiting for the reply. The script is
|
|
||||||
// evaluated with the EVALSHA command. The application must ensure that the
|
|
||||||
// script is loaded by a previous call to Send, Do or Load methods.
|
|
||||||
func (s *Script) SendHash(c Conn, keysAndArgs ...interface{}) error {
|
|
||||||
return c.Send("EVALSHA", s.args(s.hash, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send evaluates the script without waiting for the reply.
|
|
||||||
func (s *Script) Send(c Conn, keysAndArgs ...interface{}) error {
|
|
||||||
return c.Send("EVAL", s.args(s.src, keysAndArgs)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load loads the script without evaluating it.
|
|
||||||
func (s *Script) Load(c Conn) error {
|
|
||||||
_, err := c.Do("SCRIPT", "LOAD", s.src)
|
|
||||||
return err
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
@ -16,14 +16,14 @@ var clients = flag.Int("c", 50, "number of clients")
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
var pool *redis.Pool
|
var client *ledis.Client
|
||||||
|
|
||||||
var loop int = 0
|
var loop int = 0
|
||||||
|
|
||||||
func waitBench(cmd string, args ...interface{}) {
|
func waitBench(cmd string, args ...interface{}) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
c := pool.Get()
|
c := client.Get()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
for i := 0; i < loop; i++ {
|
for i := 0; i < loop; i++ {
|
||||||
|
@ -234,15 +234,9 @@ func main() {
|
||||||
|
|
||||||
addr := fmt.Sprintf("%s:%d", *ip, *port)
|
addr := fmt.Sprintf("%s:%d", *ip, *port)
|
||||||
|
|
||||||
f := func() (redis.Conn, error) {
|
cfg := new(ledis.Config)
|
||||||
c, err := redis.Dial("tcp", addr)
|
cfg.Addr = addr
|
||||||
if err != nil {
|
client = ledis.NewClient(cfg)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
pool = redis.NewPool(f, *clients)
|
|
||||||
|
|
||||||
benchSet()
|
benchSet()
|
||||||
benchIncr()
|
benchIncr()
|
||||||
|
|
|
@ -232,13 +232,13 @@ func (db *DB) HMset(key []byte, args ...FVPair) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) HMget(key []byte, args ...[]byte) ([]interface{}, error) {
|
func (db *DB) HMget(key []byte, args ...[]byte) ([][]byte, error) {
|
||||||
var ek []byte
|
var ek []byte
|
||||||
|
|
||||||
it := db.db.NewIterator()
|
it := db.db.NewIterator()
|
||||||
defer it.Close()
|
defer it.Close()
|
||||||
|
|
||||||
r := make([]interface{}, len(args))
|
r := make([][]byte, len(args))
|
||||||
for i := 0; i < len(args); i++ {
|
for i := 0; i < len(args); i++ {
|
||||||
if err := checkHashKFSize(key, args[i]); err != nil {
|
if err := checkHashKFSize(key, args[i]); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -344,7 +344,7 @@ func (db *DB) HIncrBy(key []byte, field []byte, delta int64) (int64, error) {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) HGetAll(key []byte) ([]interface{}, error) {
|
func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
|
||||||
if err := checkKeySize(key); err != nil {
|
if err := checkKeySize(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -352,16 +352,16 @@ func (db *DB) HGetAll(key []byte) ([]interface{}, error) {
|
||||||
start := db.hEncodeStartKey(key)
|
start := db.hEncodeStartKey(key)
|
||||||
stop := db.hEncodeStopKey(key)
|
stop := db.hEncodeStopKey(key)
|
||||||
|
|
||||||
v := make([]interface{}, 0, 16)
|
v := make([]FVPair, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
_, k, err := db.hDecodeHashKey(it.Key())
|
_, f, err := db.hDecodeHashKey(it.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v = append(v, k)
|
|
||||||
v = append(v, it.Value())
|
v = append(v, FVPair{Field: f, Value: it.Value()})
|
||||||
}
|
}
|
||||||
|
|
||||||
it.Close()
|
it.Close()
|
||||||
|
@ -369,7 +369,7 @@ func (db *DB) HGetAll(key []byte) ([]interface{}, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) HKeys(key []byte) ([]interface{}, error) {
|
func (db *DB) HKeys(key []byte) ([][]byte, error) {
|
||||||
if err := checkKeySize(key); err != nil {
|
if err := checkKeySize(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -377,15 +377,15 @@ func (db *DB) HKeys(key []byte) ([]interface{}, error) {
|
||||||
start := db.hEncodeStartKey(key)
|
start := db.hEncodeStartKey(key)
|
||||||
stop := db.hEncodeStopKey(key)
|
stop := db.hEncodeStopKey(key)
|
||||||
|
|
||||||
v := make([]interface{}, 0, 16)
|
v := make([][]byte, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
_, k, err := db.hDecodeHashKey(it.Key())
|
_, f, err := db.hDecodeHashKey(it.Key())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v = append(v, k)
|
v = append(v, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
it.Close()
|
it.Close()
|
||||||
|
@ -393,7 +393,7 @@ func (db *DB) HKeys(key []byte) ([]interface{}, error) {
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) HValues(key []byte) ([]interface{}, error) {
|
func (db *DB) HValues(key []byte) ([][]byte, error) {
|
||||||
if err := checkKeySize(key); err != nil {
|
if err := checkKeySize(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -401,10 +401,15 @@ func (db *DB) HValues(key []byte) ([]interface{}, error) {
|
||||||
start := db.hEncodeStartKey(key)
|
start := db.hEncodeStartKey(key)
|
||||||
stop := db.hEncodeStopKey(key)
|
stop := db.hEncodeStopKey(key)
|
||||||
|
|
||||||
v := make([]interface{}, 0, 16)
|
v := make([][]byte, 0, 16)
|
||||||
|
|
||||||
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
it := db.db.RangeLimitIterator(start, stop, leveldb.RangeROpen, 0, -1)
|
||||||
for ; it.Valid(); it.Next() {
|
for ; it.Valid(); it.Next() {
|
||||||
|
_, _, err := db.hDecodeHashKey(it.Key())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
v = append(v, it.Value())
|
v = append(v, it.Value())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -477,7 +482,7 @@ func (db *DB) HScan(key []byte, field []byte, count int, inclusive bool) ([]FVPa
|
||||||
count = defaultScanCount
|
count = defaultScanCount
|
||||||
}
|
}
|
||||||
|
|
||||||
v := make([]FVPair, 0, 2*count)
|
v := make([]FVPair, 0, count)
|
||||||
|
|
||||||
rangeType := leveldb.RangeROpen
|
rangeType := leveldb.RangeROpen
|
||||||
if !inclusive {
|
if !inclusive {
|
||||||
|
|
|
@ -46,11 +46,11 @@ func TestDBHash(t *testing.T) {
|
||||||
|
|
||||||
ay, _ := db.HMget(key, []byte("a"), []byte("b"))
|
ay, _ := db.HMget(key, []byte("a"), []byte("b"))
|
||||||
|
|
||||||
if v1, _ := ay[0].([]byte); string(v1) != "hello world 1" {
|
if v1 := ay[0]; string(v1) != "hello world 1" {
|
||||||
t.Fatal(string(v1))
|
t.Fatal(string(v1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if v2, _ := ay[1].([]byte); string(v2) != "hello world 2" {
|
if v2 := ay[1]; string(v2) != "hello world 2" {
|
||||||
t.Fatal(string(v2))
|
t.Fatal(string(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -201,8 +201,8 @@ func (db *DB) IncryBy(key []byte, increment int64) (int64, error) {
|
||||||
return db.incr(key, increment)
|
return db.incr(key, increment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) MGet(keys ...[]byte) ([]interface{}, error) {
|
func (db *DB) MGet(keys ...[]byte) ([][]byte, error) {
|
||||||
values := make([]interface{}, len(keys))
|
values := make([][]byte, len(keys))
|
||||||
|
|
||||||
it := db.db.NewIterator()
|
it := db.db.NewIterator()
|
||||||
defer it.Close()
|
defer it.Close()
|
||||||
|
|
|
@ -33,11 +33,11 @@ func TestDBKV(t *testing.T) {
|
||||||
|
|
||||||
ay, _ := db.MGet(key1, key2)
|
ay, _ := db.MGet(key1, key2)
|
||||||
|
|
||||||
if v1, _ := ay[0].([]byte); string(v1) != "hello world 1" {
|
if v1 := ay[0]; string(v1) != "hello world 1" {
|
||||||
t.Fatal(string(v1))
|
t.Fatal(string(v1))
|
||||||
}
|
}
|
||||||
|
|
||||||
if v2, _ := ay[1].([]byte); string(v2) != "hello world 2" {
|
if v2 := ay[1]; string(v2) != "hello world 2" {
|
||||||
t.Fatal(string(v2))
|
t.Fatal(string(v2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,20 +317,20 @@ func (db *DB) LPush(key []byte, args ...[]byte) (int64, error) {
|
||||||
return db.lpush(key, listHeadSeq, args...)
|
return db.lpush(key, listHeadSeq, args...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) LRange(key []byte, start int32, stop int32) ([]interface{}, error) {
|
func (db *DB) LRange(key []byte, start int32, stop int32) ([][]byte, error) {
|
||||||
if err := checkKeySize(key); err != nil {
|
if err := checkKeySize(key); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
v := make([]interface{}, 0, 16)
|
|
||||||
|
|
||||||
var startSeq int32
|
var startSeq int32
|
||||||
var stopSeq int32
|
var stopSeq int32
|
||||||
|
|
||||||
if start > stop {
|
if start > stop {
|
||||||
return []interface{}{}, nil
|
return [][]byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
v := make([][]byte, 0, 16)
|
||||||
|
|
||||||
var headSeq int32
|
var headSeq int32
|
||||||
var tailSeq int32
|
var tailSeq int32
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -534,38 +534,21 @@ func (db *DB) zRemRange(t *tx, key []byte, min int64, max int64, offset int, lim
|
||||||
return num, nil
|
return num, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) zReverse(s []interface{}, withScores bool) []interface{} {
|
func (db *DB) zRange(key []byte, min int64, max int64, offset int, limit int, reverse bool) ([]ScorePair, error) {
|
||||||
if withScores {
|
|
||||||
for i, j := 0, len(s)-2; i < j; i, j = i+2, j-2 {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
s[i+1], s[j+1] = s[j+1], s[i+1]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 {
|
|
||||||
s[i], s[j] = s[j], s[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (db *DB) zRange(key []byte, min int64, max int64, withScores bool, offset int, limit int, reverse bool) ([]interface{}, error) {
|
|
||||||
if len(key) > MaxKeySize {
|
if len(key) > MaxKeySize {
|
||||||
return nil, errKeySize
|
return nil, errKeySize
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
return []interface{}{}, nil
|
return []ScorePair{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
nv := 64
|
nv := 64
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
nv = limit
|
nv = limit
|
||||||
}
|
}
|
||||||
if withScores {
|
|
||||||
nv = 2 * nv
|
v := make([]ScorePair, 0, nv)
|
||||||
}
|
|
||||||
v := make([]interface{}, 0, nv)
|
|
||||||
|
|
||||||
var it *leveldb.RangeLimitIterator
|
var it *leveldb.RangeLimitIterator
|
||||||
|
|
||||||
|
@ -584,16 +567,14 @@ func (db *DB) zRange(key []byte, min int64, max int64, withScores bool, offset i
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if withScores {
|
v = append(v, ScorePair{Member: m, Score: s})
|
||||||
v = append(v, m, s)
|
|
||||||
} else {
|
|
||||||
v = append(v, m)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
it.Close()
|
it.Close()
|
||||||
|
|
||||||
if reverse && (offset == 0 && limit < 0) {
|
if reverse && (offset == 0 && limit < 0) {
|
||||||
v = db.zReverse(v, withScores)
|
for i, j := 0, len(v)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
v[i], v[j] = v[j], v[i]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return v, nil
|
return v, nil
|
||||||
|
@ -650,15 +631,15 @@ func (db *DB) ZClear(key []byte) (int64, error) {
|
||||||
return rmCnt, err
|
return rmCnt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) ZRange(key []byte, start int, stop int, withScores bool) ([]interface{}, error) {
|
func (db *DB) ZRange(key []byte, start int, stop int) ([]ScorePair, error) {
|
||||||
return db.ZRangeGeneric(key, start, stop, withScores, false)
|
return db.ZRangeGeneric(key, start, stop, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
//min and max must be inclusive
|
//min and max must be inclusive
|
||||||
//if no limit, set offset = 0 and count = -1
|
//if no limit, set offset = 0 and count = -1
|
||||||
func (db *DB) ZRangeByScore(key []byte, min int64, max int64,
|
func (db *DB) ZRangeByScore(key []byte, min int64, max int64,
|
||||||
withScores bool, offset int, count int) ([]interface{}, error) {
|
offset int, count int) ([]ScorePair, error) {
|
||||||
return db.ZRangeByScoreGeneric(key, min, max, withScores, offset, count, false)
|
return db.ZRangeByScoreGeneric(key, min, max, offset, count, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) ZRank(key []byte, member []byte) (int64, error) {
|
func (db *DB) ZRank(key []byte, member []byte) (int64, error) {
|
||||||
|
@ -699,8 +680,8 @@ func (db *DB) ZRemRangeByScore(key []byte, min int64, max int64) (int64, error)
|
||||||
return rmCnt, err
|
return rmCnt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) ZRevRange(key []byte, start int, stop int, withScores bool) ([]interface{}, error) {
|
func (db *DB) ZRevRange(key []byte, start int, stop int) ([]ScorePair, error) {
|
||||||
return db.ZRangeGeneric(key, start, stop, withScores, true)
|
return db.ZRangeGeneric(key, start, stop, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) ZRevRank(key []byte, member []byte) (int64, error) {
|
func (db *DB) ZRevRank(key []byte, member []byte) (int64, error) {
|
||||||
|
@ -709,27 +690,25 @@ func (db *DB) ZRevRank(key []byte, member []byte) (int64, error) {
|
||||||
|
|
||||||
//min and max must be inclusive
|
//min and max must be inclusive
|
||||||
//if no limit, set offset = 0 and count = -1
|
//if no limit, set offset = 0 and count = -1
|
||||||
func (db *DB) ZRevRangeByScore(key []byte, min int64, max int64,
|
func (db *DB) ZRevRangeByScore(key []byte, min int64, max int64, offset int, count int) ([]ScorePair, error) {
|
||||||
withScores bool, offset int, count int) ([]interface{}, error) {
|
return db.ZRangeByScoreGeneric(key, min, max, offset, count, true)
|
||||||
return db.ZRangeByScoreGeneric(key, min, max, withScores, offset, count, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) ZRangeGeneric(key []byte, start int, stop int,
|
func (db *DB) ZRangeGeneric(key []byte, start int, stop int, reverse bool) ([]ScorePair, error) {
|
||||||
withScores bool, reverse bool) ([]interface{}, error) {
|
|
||||||
offset, limit, err := db.zParseLimit(key, start, stop)
|
offset, limit, err := db.zParseLimit(key, start, stop)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.zRange(key, MinScore, MaxScore, withScores, offset, limit, reverse)
|
return db.zRange(key, MinScore, MaxScore, offset, limit, reverse)
|
||||||
}
|
}
|
||||||
|
|
||||||
//min and max must be inclusive
|
//min and max must be inclusive
|
||||||
//if no limit, set offset = 0 and count = -1
|
//if no limit, set offset = 0 and count = -1
|
||||||
func (db *DB) ZRangeByScoreGeneric(key []byte, min int64, max int64,
|
func (db *DB) ZRangeByScoreGeneric(key []byte, min int64, max int64,
|
||||||
withScores bool, offset int, count int, reverse bool) ([]interface{}, error) {
|
offset int, count int, reverse bool) ([]ScorePair, error) {
|
||||||
|
|
||||||
return db.zRange(key, min, max, withScores, offset, count, reverse)
|
return db.zRange(key, min, max, offset, count, reverse)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *DB) zFlush() (drop int64, err error) {
|
func (db *DB) zFlush() (drop int64, err error) {
|
||||||
|
|
|
@ -145,15 +145,13 @@ func TestZSetOrder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if qMembs, err := db.ZRange(key, 0, endPos, false); err != nil {
|
if qMembs, err := db.ZRange(key, 0, endPos); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(qMembs) != membCnt {
|
} else if len(qMembs) != membCnt {
|
||||||
t.Fatal(fmt.Sprintf("%d vs %d", len(qMembs), membCnt))
|
t.Fatal(fmt.Sprintf("%d vs %d", len(qMembs), membCnt))
|
||||||
} else {
|
} else {
|
||||||
for i := 0; i < membCnt; i++ {
|
for i := 0; i < membCnt; i++ {
|
||||||
if memb, ok := qMembs[i].([]byte); !ok {
|
if string(qMembs[i].Member) != membs[i] {
|
||||||
t.Fatal(ok)
|
|
||||||
} else if string(memb) != membs[i] {
|
|
||||||
t.Fatal(fmt.Sprintf("[%s] vs [%s]", qMembs[i], membs[i]))
|
t.Fatal(fmt.Sprintf("[%s] vs [%s]", qMembs[i], membs[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,7 +180,7 @@ func TestZSetOrder(t *testing.T) {
|
||||||
t.Fatal(pos)
|
t.Fatal(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
if qMembs, err := db.ZRangeByScore(key, 999, 0XFFFF, false, 0, membCnt); err != nil {
|
if qMembs, err := db.ZRangeByScore(key, 999, 0XFFFF, 0, membCnt); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(qMembs) != 1 {
|
} else if len(qMembs) != 1 {
|
||||||
t.Fatal(len(qMembs))
|
t.Fatal(len(qMembs))
|
||||||
|
@ -203,12 +201,12 @@ func TestZSetOrder(t *testing.T) {
|
||||||
t.Fatal(pos)
|
t.Fatal(pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
if datas, _ := db.ZRange(key, 0, endPos, true); len(datas) != 12 {
|
if datas, _ := db.ZRange(key, 0, endPos); len(datas) != 6 {
|
||||||
t.Fatal(len(datas))
|
t.Fatal(len(datas))
|
||||||
} else {
|
} else {
|
||||||
scores := []int64{0, 1, 2, 5, 6, 999}
|
scores := []int64{0, 1, 2, 5, 6, 999}
|
||||||
for i := 1; i < len(datas); i += 2 {
|
for i := 0; i < len(datas); i++ {
|
||||||
if s, ok := datas[i].(int64); !ok || s != scores[(i-1)/2] {
|
if datas[i].Score != scores[i] {
|
||||||
t.Fatal(fmt.Sprintf("[%d]=%d", i, datas[i]))
|
t.Fatal(fmt.Sprintf("[%d]=%d", i, datas[i]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -10,29 +10,23 @@ import (
|
||||||
var testAppOnce sync.Once
|
var testAppOnce sync.Once
|
||||||
var testApp *App
|
var testApp *App
|
||||||
|
|
||||||
var testPool *redis.Pool
|
var testLedisClient *ledis.Client
|
||||||
|
|
||||||
func newTestRedisPool() {
|
func newTestLedisClient() {
|
||||||
f := func() (redis.Conn, error) {
|
cfg := new(ledis.Config)
|
||||||
c, err := redis.Dial("tcp", "127.0.0.1:16380")
|
cfg.Addr = "127.0.0.1:16380"
|
||||||
if err != nil {
|
cfg.MaxIdleConns = 4
|
||||||
return nil, err
|
testLedisClient = ledis.NewClient(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
func getTestConn() *ledis.Conn {
|
||||||
}
|
|
||||||
|
|
||||||
testPool = redis.NewPool(f, 4)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTestConn() redis.Conn {
|
|
||||||
startTestApp()
|
startTestApp()
|
||||||
return testPool.Get()
|
return testLedisClient.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
func startTestApp() {
|
func startTestApp() {
|
||||||
f := func() {
|
f := func() {
|
||||||
newTestRedisPool()
|
newTestLedisClient()
|
||||||
|
|
||||||
os.RemoveAll("/tmp/testdb")
|
os.RemoveAll("/tmp/testdb")
|
||||||
|
|
||||||
|
|
|
@ -245,6 +245,62 @@ func (c *client) writeArray(ay []interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *client) writeSliceArray(ay [][]byte) {
|
||||||
|
c.wb.WriteByte('*')
|
||||||
|
if ay == nil {
|
||||||
|
c.wb.Write(NullArray)
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
} else {
|
||||||
|
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay))))
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
|
||||||
|
for i := 0; i < len(ay); i++ {
|
||||||
|
c.writeBulk(ay[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) writeFVPairArray(ay []ledis.FVPair) {
|
||||||
|
c.wb.WriteByte('*')
|
||||||
|
if ay == nil {
|
||||||
|
c.wb.Write(NullArray)
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
} else {
|
||||||
|
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay) * 2)))
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
|
||||||
|
for i := 0; i < len(ay); i++ {
|
||||||
|
c.writeBulk(ay[i].Field)
|
||||||
|
c.writeBulk(ay[i].Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *client) writeScorePairArray(ay []ledis.ScorePair, withScores bool) {
|
||||||
|
c.wb.WriteByte('*')
|
||||||
|
if ay == nil {
|
||||||
|
c.wb.Write(NullArray)
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
} else {
|
||||||
|
if withScores {
|
||||||
|
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay) * 2)))
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
} else {
|
||||||
|
c.wb.Write(ledis.Slice(strconv.Itoa(len(ay))))
|
||||||
|
c.wb.Write(Delims)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(ay); i++ {
|
||||||
|
c.writeBulk(ay[i].Member)
|
||||||
|
|
||||||
|
if withScores {
|
||||||
|
c.writeBulk(ledis.StrPutInt64(ay[i].Score))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *client) writeBulkFrom(n int64, rb io.Reader) {
|
func (c *client) writeBulkFrom(n int64, rb io.Reader) {
|
||||||
c.wb.WriteByte('$')
|
c.wb.WriteByte('$')
|
||||||
c.wb.Write(ledis.Slice(strconv.FormatInt(n, 10)))
|
c.wb.Write(ledis.Slice(strconv.FormatInt(n, 10)))
|
||||||
|
|
|
@ -141,7 +141,7 @@ func hmgetCommand(c *client) error {
|
||||||
if v, err := c.db.HMget(args[0], args[1:]...); err != nil {
|
if v, err := c.db.HMget(args[0], args[1:]...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -156,7 +156,7 @@ func hgetallCommand(c *client) error {
|
||||||
if v, err := c.db.HGetAll(args[0]); err != nil {
|
if v, err := c.db.HGetAll(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeFVPairArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -171,7 +171,7 @@ func hkeysCommand(c *client) error {
|
||||||
if v, err := c.db.HKeys(args[0]); err != nil {
|
if v, err := c.db.HKeys(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -186,7 +186,7 @@ func hvalsCommand(c *client) error {
|
||||||
if v, err := c.db.HValues(args[0]); err != nil {
|
if v, err := c.db.HValues(args[0]); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,7 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -12,43 +12,43 @@ func TestHash(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("a")
|
key := []byte("a")
|
||||||
if n, err := redis.Int(c.Do("hset", key, 1, 0)); err != nil {
|
if n, err := ledis.Int(c.Do("hset", key, 1, 0)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hexists", key, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("hexists", key, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hexists", key, -1)); err != nil {
|
if n, err := ledis.Int(c.Do("hexists", key, -1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hget", key, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("hget", key, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hset", key, 1, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("hset", key, 1, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hget", key, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("hget", key, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -84,19 +84,19 @@ func TestHashM(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("b")
|
key := []byte("b")
|
||||||
if ok, err := redis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
|
if ok, err := ledis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ok != OK {
|
} else if ok != OK {
|
||||||
t.Fatal(ok)
|
t.Fatal(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testHashArray(v, 1, 2, 3, 0); err != nil {
|
if err := testHashArray(v, 1, 2, 3, 0); err != nil {
|
||||||
|
@ -104,19 +104,19 @@ func TestHashM(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hdel", key, 1, 2, 3, 4)); err != nil {
|
if n, err := ledis.Int(c.Do("hdel", key, 1, 2, 3, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("hmget", key, 1, 2, 3, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testHashArray(v, 0, 0, 0, 0); err != nil {
|
if err := testHashArray(v, 0, 0, 0, 0); err != nil {
|
||||||
|
@ -124,7 +124,7 @@ func TestHashM(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -136,31 +136,31 @@ func TestHashIncr(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("c")
|
key := []byte("c")
|
||||||
if n, err := redis.Int(c.Do("hincrby", key, 1, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("hincrby", key, 1, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hincrby", key, 1, 10)); err != nil {
|
if n, err := ledis.Int(c.Do("hincrby", key, 1, 10)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 11 {
|
} else if n != 11 {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hincrby", key, 1, -11)); err != nil {
|
if n, err := ledis.Int(c.Do("hincrby", key, 1, -11)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
@ -174,13 +174,13 @@ func TestHashGetAll(t *testing.T) {
|
||||||
|
|
||||||
key := []byte("d")
|
key := []byte("d")
|
||||||
|
|
||||||
if ok, err := redis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
|
if ok, err := ledis.String(c.Do("hmset", key, 1, 1, 2, 2, 3, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ok != OK {
|
} else if ok != OK {
|
||||||
t.Fatal(ok)
|
t.Fatal(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("hgetall", key)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("hgetall", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testHashArray(v, 1, 1, 2, 2, 3, 3); err != nil {
|
if err := testHashArray(v, 1, 1, 2, 2, 3, 3); err != nil {
|
||||||
|
@ -188,7 +188,7 @@ func TestHashGetAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("hkeys", key)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("hkeys", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testHashArray(v, 1, 2, 3); err != nil {
|
if err := testHashArray(v, 1, 2, 3); err != nil {
|
||||||
|
@ -196,7 +196,7 @@ func TestHashGetAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("hvals", key)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("hvals", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testHashArray(v, 1, 2, 3); err != nil {
|
if err := testHashArray(v, 1, 2, 3); err != nil {
|
||||||
|
@ -204,13 +204,13 @@ func TestHashGetAll(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hclear", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hclear", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("hlen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("hlen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
|
|
@ -197,7 +197,7 @@ func mgetCommand(c *client) error {
|
||||||
if v, err := c.db.MGet(args...); err != nil {
|
if v, err := c.db.MGet(args...); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -9,65 +9,65 @@ func TestKV(t *testing.T) {
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if ok, err := redis.String(c.Do("set", "a", "1234")); err != nil {
|
if ok, err := ledis.String(c.Do("set", "a", "1234")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ok != OK {
|
} else if ok != OK {
|
||||||
t.Fatal(ok)
|
t.Fatal(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("setnx", "a", "123")); err != nil {
|
if n, err := ledis.Int(c.Do("setnx", "a", "123")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("setnx", "b", "123")); err != nil {
|
if n, err := ledis.Int(c.Do("setnx", "b", "123")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.String(c.Do("get", "a")); err != nil {
|
if v, err := ledis.String(c.Do("get", "a")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != "1234" {
|
} else if v != "1234" {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.String(c.Do("getset", "a", "123")); err != nil {
|
if v, err := ledis.String(c.Do("getset", "a", "123")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != "1234" {
|
} else if v != "1234" {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.String(c.Do("get", "a")); err != nil {
|
if v, err := ledis.String(c.Do("get", "a")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != "123" {
|
} else if v != "123" {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("exists", "a")); err != nil {
|
if n, err := ledis.Int(c.Do("exists", "a")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("exists", "empty_key_test")); err != nil {
|
if n, err := ledis.Int(c.Do("exists", "empty_key_test")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := redis.Int(c.Do("del", "a", "b")); err != nil {
|
if _, err := ledis.Int(c.Do("del", "a", "b")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("exists", "a")); err != nil {
|
if n, err := ledis.Int(c.Do("exists", "a")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("exists", "b")); err != nil {
|
if n, err := ledis.Int(c.Do("exists", "b")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -78,13 +78,13 @@ func TestKVM(t *testing.T) {
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if ok, err := redis.String(c.Do("mset", "a", "1", "b", "2")); err != nil {
|
if ok, err := ledis.String(c.Do("mset", "a", "1", "b", "2")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ok != OK {
|
} else if ok != OK {
|
||||||
t.Fatal(ok)
|
t.Fatal(ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("mget", "a", "b", "c")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("mget", "a", "b", "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 3 {
|
} else if len(v) != 3 {
|
||||||
t.Fatal(len(v))
|
t.Fatal(len(v))
|
||||||
|
@ -107,31 +107,31 @@ func TestKVIncrDecr(t *testing.T) {
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
if n, err := redis.Int64(c.Do("incr", "n")); err != nil {
|
if n, err := ledis.Int64(c.Do("incr", "n")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int64(c.Do("incr", "n")); err != nil {
|
if n, err := ledis.Int64(c.Do("incr", "n")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int64(c.Do("decr", "n")); err != nil {
|
if n, err := ledis.Int64(c.Do("decr", "n")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int64(c.Do("incrby", "n", 10)); err != nil {
|
if n, err := ledis.Int64(c.Do("incrby", "n", 10)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 11 {
|
} else if n != 11 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int64(c.Do("decrby", "n", 10)); err != nil {
|
if n, err := ledis.Int64(c.Do("decrby", "n", 10)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
|
|
@ -122,7 +122,7 @@ func lrangeCommand(c *client) error {
|
||||||
if v, err := c.db.LRange(args[0], int32(start), int32(stop)); err != nil {
|
if v, err := c.db.LRange(args[0], int32(start), int32(stop)); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
c.writeArray(v)
|
c.writeSliceArray(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,7 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -11,10 +11,10 @@ func testListIndex(key []byte, index int64, v int) error {
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
n, err := redis.Int(c.Do("lindex", key, index))
|
n, err := ledis.Int(c.Do("lindex", key, index))
|
||||||
if err == redis.ErrNil && v != 0 {
|
if err == ledis.ErrNil && v != 0 {
|
||||||
return fmt.Errorf("must nil")
|
return fmt.Errorf("must nil")
|
||||||
} else if err != nil && err != redis.ErrNil {
|
} else if err != nil && err != ledis.ErrNil {
|
||||||
return err
|
return err
|
||||||
} else if n != v {
|
} else if n != v {
|
||||||
return fmt.Errorf("index err number %d != %d", n, v)
|
return fmt.Errorf("index err number %d != %d", n, v)
|
||||||
|
@ -27,7 +27,7 @@ func testListRange(key []byte, start int64, stop int64, checkValues ...int) erro
|
||||||
c := getTestConn()
|
c := getTestConn()
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
vs, err := redis.MultiBulk(c.Do("lrange", key, start, stop))
|
vs, err := ledis.MultiBulk(c.Do("lrange", key, start, stop))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -59,31 +59,31 @@ func TestList(t *testing.T) {
|
||||||
|
|
||||||
key := []byte("a")
|
key := []byte("a")
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("lpush", key, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("lpush", key, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("rpush", key, 2)); err != nil {
|
if n, err := ledis.Int(c.Do("rpush", key, 2)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("rpush", key, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("rpush", key, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("llen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
//for redis-cli a 1 2 3
|
//for ledis-cli a 1 2 3
|
||||||
// 127.0.0.1:6379> lrange a 0 0
|
// 127.0.0.1:6379> lrange a 0 0
|
||||||
// 1) "1"
|
// 1) "1"
|
||||||
if err := testListRange(key, 0, 0, 1); err != nil {
|
if err := testListRange(key, 0, 0, 1); err != nil {
|
||||||
|
@ -201,7 +201,7 @@ func TestListMPush(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("b")
|
key := []byte("b")
|
||||||
if n, err := redis.Int(c.Do("rpush", key, 1, 2, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("rpush", key, 1, 2, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -211,7 +211,7 @@ func TestListMPush(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("lpush", key, 1, 2, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("lpush", key, 1, 2, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 6 {
|
} else if n != 6 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -227,25 +227,25 @@ func TestPop(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("c")
|
key := []byte("c")
|
||||||
if n, err := redis.Int(c.Do("rpush", key, 1, 2, 3, 4, 5, 6)); err != nil {
|
if n, err := ledis.Int(c.Do("rpush", key, 1, 2, 3, 4, 5, 6)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 6 {
|
} else if n != 6 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.Int(c.Do("lpop", key)); err != nil {
|
if v, err := ledis.Int(c.Do("lpop", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != 1 {
|
} else if v != 1 {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.Int(c.Do("rpop", key)); err != nil {
|
if v, err := ledis.Int(c.Do("rpop", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != 6 {
|
} else if v != 6 {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("lpush", key, 1)); err != nil {
|
if n, err := ledis.Int(c.Do("lpush", key, 1)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 5 {
|
} else if n != 5 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -256,14 +256,14 @@ func TestPop(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i <= 5; i++ {
|
for i := 1; i <= 5; i++ {
|
||||||
if v, err := redis.Int(c.Do("lpop", key)); err != nil {
|
if v, err := ledis.Int(c.Do("lpop", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if v != i {
|
} else if v != i {
|
||||||
t.Fatal(v)
|
t.Fatal(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("llen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -271,13 +271,13 @@ func TestPop(t *testing.T) {
|
||||||
|
|
||||||
c.Do("rpush", key, 1, 2, 3, 4, 5)
|
c.Do("rpush", key, 1, 2, 3, 4, 5)
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("lclear", key)); err != nil {
|
if n, err := ledis.Int(c.Do("lclear", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 5 {
|
} else if n != 5 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("llen", key)); err != nil {
|
if n, err := ledis.Int(c.Do("llen", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -19,13 +19,13 @@ func TestKVExpire(t *testing.T) {
|
||||||
|
|
||||||
// expire + ttl
|
// expire + ttl
|
||||||
exp := int64(10)
|
exp := int64(10)
|
||||||
if n, err := redis.Int(c.Do("expire", k, exp)); err != nil {
|
if n, err := ledis.Int(c.Do("expire", k, exp)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ttl, err := redis.Int64(c.Do("ttl", k)); err != nil {
|
if ttl, err := ledis.Int64(c.Do("ttl", k)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ttl != exp {
|
} else if ttl != exp {
|
||||||
t.Fatal(ttl)
|
t.Fatal(ttl)
|
||||||
|
@ -33,13 +33,13 @@ func TestKVExpire(t *testing.T) {
|
||||||
|
|
||||||
// expireat + ttl
|
// expireat + ttl
|
||||||
tm := now() + 3
|
tm := now() + 3
|
||||||
if n, err := redis.Int(c.Do("expireat", k, tm)); err != nil {
|
if n, err := ledis.Int(c.Do("expireat", k, tm)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ttl, err := redis.Int64(c.Do("ttl", k)); err != nil {
|
if ttl, err := ledis.Int64(c.Do("ttl", k)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if ttl != 3 {
|
} else if ttl != 3 {
|
||||||
t.Fatal(ttl)
|
t.Fatal(ttl)
|
||||||
|
@ -48,15 +48,15 @@ func TestKVExpire(t *testing.T) {
|
||||||
kErr := "not_exist_ttl"
|
kErr := "not_exist_ttl"
|
||||||
|
|
||||||
// err - expire, expireat
|
// err - expire, expireat
|
||||||
if n, err := redis.Int(c.Do("expire", kErr, tm)); err != nil || n != 0 {
|
if n, err := ledis.Int(c.Do("expire", kErr, tm)); err != nil || n != 0 {
|
||||||
t.Fatal(false)
|
t.Fatal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("expireat", kErr, tm)); err != nil || n != 0 {
|
if n, err := ledis.Int(c.Do("expireat", kErr, tm)); err != nil || n != 0 {
|
||||||
t.Fatal(false)
|
t.Fatal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("ttl", kErr)); err != nil || n != -1 {
|
if n, err := ledis.Int(c.Do("ttl", kErr)); err != nil || n != -1 {
|
||||||
t.Fatal(false)
|
t.Fatal(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -312,16 +312,10 @@ func zrangeGeneric(c *client, reverse bool) error {
|
||||||
withScores = true
|
withScores = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if datas, err := c.db.ZRangeGeneric(key, start, stop, withScores, reverse); err != nil {
|
if datas, err := c.db.ZRangeGeneric(key, start, stop, reverse); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if withScores {
|
c.writeScorePairArray(datas, withScores)
|
||||||
for i := len(datas) - 1; i > 0; i -= 2 {
|
|
||||||
v, _ := datas[i].(int64)
|
|
||||||
datas[i] = ledis.StrPutInt64(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.writeArray(datas)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -377,22 +371,16 @@ func zrangebyscoreGeneric(c *client, reverse bool) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if offset < 0 {
|
if offset < 0 {
|
||||||
//for redis, if offset < 0, a empty will return
|
//for ledis, if offset < 0, a empty will return
|
||||||
//so here we directly return a empty array
|
//so here we directly return a empty array
|
||||||
c.writeArray([]interface{}{})
|
c.writeArray([]interface{}{})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if datas, err := c.db.ZRangeByScoreGeneric(key, min, max, withScores, offset, count, reverse); err != nil {
|
if datas, err := c.db.ZRangeByScoreGeneric(key, min, max, offset, count, reverse); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
if withScores {
|
c.writeScorePairArray(datas, withScores)
|
||||||
for i := len(datas) - 1; i > 0; i -= 2 {
|
|
||||||
v, _ := datas[i].(int64)
|
|
||||||
datas[i] = ledis.StrPutInt64(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c.writeArray(datas)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -2,7 +2,7 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/siddontang/ledisdb/client/go/redis"
|
"github.com/siddontang/ledisdb/client/go/ledis"
|
||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -12,91 +12,91 @@ func TestZSet(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("myzset")
|
key := []byte("myzset")
|
||||||
if n, err := redis.Int(c.Do("zadd", key, 3, "a", 4, "b")); err != nil {
|
if n, err := ledis.Int(c.Do("zadd", key, 3, "a", 4, "b")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zadd", key, 1, "a", 2, "b")); err != nil {
|
if n, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zadd", key, 3, "c", 4, "d")); err != nil {
|
if n, err := ledis.Int(c.Do("zadd", key, 3, "c", 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 4 {
|
} else if n != 4 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if s, err := redis.Int(c.Do("zscore", key, "c")); err != nil {
|
if s, err := ledis.Int(c.Do("zscore", key, "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if s != 3 {
|
} else if s != 3 {
|
||||||
t.Fatal(s)
|
t.Fatal(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zrem", key, "d", "e")); err != nil {
|
if n, err := ledis.Int(c.Do("zrem", key, "d", "e")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zincrby", key, 4, "c")); err != nil {
|
if n, err := ledis.Int(c.Do("zincrby", key, 4, "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 7 {
|
} else if n != 7 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zincrby", key, -4, "c")); err != nil {
|
if n, err := ledis.Int(c.Do("zincrby", key, -4, "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zincrby", key, 4, "d")); err != nil {
|
if n, err := ledis.Int(c.Do("zincrby", key, 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 4 {
|
} else if n != 4 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 4 {
|
} else if n != 4 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zrem", key, "a", "b", "c", "d")); err != nil {
|
if n, err := ledis.Int(c.Do("zrem", key, "a", "b", "c", "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 4 {
|
} else if n != 4 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -109,47 +109,47 @@ func TestZSetCount(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("myzset")
|
key := []byte("myzset")
|
||||||
if _, err := redis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, 2, 4)); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, 2, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 3 {
|
} else if n != 3 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, 4, 4)); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, 4, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, 4, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, 4, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, "(2", 4)); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, "(2", 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, "2", "(4")); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, "2", "(4")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, "-inf", "+inf")); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, "-inf", "+inf")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 4 {
|
} else if n != 4 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -157,7 +157,7 @@ func TestZSetCount(t *testing.T) {
|
||||||
|
|
||||||
c.Do("zadd", key, 3, "e")
|
c.Do("zadd", key, 3, "e")
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
|
if n, err := ledis.Int(c.Do("zcount", key, "(2", "(4")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
@ -171,27 +171,27 @@ func TestZSetRank(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("myzset")
|
key := []byte("myzset")
|
||||||
if _, err := redis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zrank", key, "c")); err != nil {
|
if n, err := ledis.Int(c.Do("zrank", key, "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := redis.Int(c.Do("zrank", key, "e")); err != redis.ErrNil {
|
if _, err := ledis.Int(c.Do("zrank", key, "e")); err != ledis.ErrNil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zrevrank", key, "c")); err != nil {
|
if n, err := ledis.Int(c.Do("zrevrank", key, "c")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 1 {
|
} else if n != 1 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := redis.Int(c.Do("zrevrank", key, "e")); err != redis.ErrNil {
|
if _, err := ledis.Int(c.Do("zrevrank", key, "e")); err != ledis.ErrNil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,11 +228,11 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("myzset_range")
|
key := []byte("myzset_range")
|
||||||
if _, err := redis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -240,7 +240,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "b", 2, "c", 3); err != nil {
|
if err := testZSetRange(v, "b", 2, "c", 3); err != nil {
|
||||||
|
@ -248,7 +248,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -256,7 +256,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrangebyscore", key, "(1", "(4")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, "(1", "(4")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "b", "c"); err != nil {
|
if err := testZSetRange(v, "b", "c"); err != nil {
|
||||||
|
@ -264,7 +264,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrangebyscore", key, 1, 4, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, 1, 4, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -272,7 +272,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, 1, 4, "withscores", "limit", 1, 2)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "c", 3, "b", 2); err != nil {
|
if err := testZSetRange(v, "c", 3, "b", 2); err != nil {
|
||||||
|
@ -280,7 +280,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, "-inf", "+inf", "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -288,7 +288,7 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrangebyscore", key, "(1", "(4")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrangebyscore", key, "(1", "(4")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "c", "b"); err != nil {
|
if err := testZSetRange(v, "c", "b"); err != nil {
|
||||||
|
@ -296,19 +296,19 @@ func TestZSetRangeScore(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zremrangebyscore", key, 2, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("zremrangebyscore", key, 2, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrangebyscore", key, 1, 4)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrangebyscore", key, 1, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", "d"); err != nil {
|
if err := testZSetRange(v, "a", "d"); err != nil {
|
||||||
|
@ -322,11 +322,11 @@ func TestZSetRange(t *testing.T) {
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
key := []byte("myzset_range_rank")
|
key := []byte("myzset_range_rank")
|
||||||
if _, err := redis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
if _, err := ledis.Int(c.Do("zadd", key, 1, "a", 2, "b", 3, "c", 4, "d")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, 0, 3, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, 3, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -334,7 +334,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, 1, 4, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, 1, 4, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "b", 2, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "b", 2, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -342,7 +342,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, -2, -1, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, -2, -1, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -350,7 +350,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, 0, -1, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, -1, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
if err := testZSetRange(v, "a", 1, "b", 2, "c", 3, "d", 4); err != nil {
|
||||||
|
@ -358,13 +358,13 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, -1, -2, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, -1, -2, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if len(v) != 0 {
|
} else if len(v) != 0 {
|
||||||
t.Fatal(len(v))
|
t.Fatal(len(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrange", key, 0, 4, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 0, 4, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -372,7 +372,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrange", key, 0, -1, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 0, -1, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "d", 4, "c", 3, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -380,7 +380,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrange", key, 2, 3, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, 2, 3, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -388,7 +388,7 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrevrange", key, -2, -1, "withscores")); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrevrange", key, -2, -1, "withscores")); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
|
if err := testZSetRange(v, "b", 2, "a", 1); err != nil {
|
||||||
|
@ -396,19 +396,19 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zremrangebyrank", key, 2, 3)); err != nil {
|
if n, err := ledis.Int(c.Do("zremrangebyrank", key, 2, 3)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if v, err := redis.MultiBulk(c.Do("zrange", key, 0, 4)); err != nil {
|
if v, err := ledis.MultiBulk(c.Do("zrange", key, 0, 4)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else {
|
} else {
|
||||||
if err := testZSetRange(v, "a", "b"); err != nil {
|
if err := testZSetRange(v, "a", "b"); err != nil {
|
||||||
|
@ -416,13 +416,13 @@ func TestZSetRange(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zclear", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zclear", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 2 {
|
} else if n != 2 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n, err := redis.Int(c.Do("zcard", key)); err != nil {
|
if n, err := ledis.Int(c.Do("zcard", key)); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
} else if n != 0 {
|
} else if n != 0 {
|
||||||
t.Fatal(n)
|
t.Fatal(n)
|
||||||
|
|
Loading…
Reference in New Issue