diff --git a/redis.go b/redis.go index 9e56ba2..918d6de 100644 --- a/redis.go +++ b/redis.go @@ -3,7 +3,6 @@ package redis import ( "bufio" "bytes" - "container/vector" "fmt" "io" "io/ioutil" @@ -93,7 +92,7 @@ func readResponse(reader *bufio.Reader) (interface{}, os.Error) { } if line[0] == ':' { - n, err := strconv.Atoi(strings.TrimSpace(line[1:])) + n, err := strconv.Atoi64(strings.TrimSpace(line[1:])) if err != nil { return nil, RedisError("Int reply is not a number") } @@ -170,12 +169,18 @@ func (client *Client) openConnection() (c *net.TCPConn, err os.Error) { return } } + //TODO: handle authentication here return } -func (client *Client) sendCommand(cmd string) (data interface{}, err os.Error) { +func (client *Client) sendCommand(cmd string, args []string) (data interface{}, err os.Error) { + cmdbuf := bytes.NewBufferString(fmt.Sprintf("*%d\r\n$%d\r\n%s\r\n", len(args)+1, len(cmd), cmd)) + for _, s := range args { + cmdbuf.WriteString(fmt.Sprintf("$%d\r\n%s\r\n", len(s), s)) + } + // grab a connection from the pool c := <-pool @@ -186,7 +191,7 @@ func (client *Client) sendCommand(cmd string) (data interface{}, err os.Error) { } } - data, err = client.rawSend(c, []byte(cmd)) + data, err = client.rawSend(c, cmdbuf.Bytes()) if err == os.EOF { c, err = client.openConnection() @@ -205,104 +210,143 @@ End: return data, err } -func (client *Client) Get(name string) ([]byte, os.Error) { - cmd := fmt.Sprintf("GET %s\r\n", name) - res, _ := client.sendCommand(cmd) +// General Commands - if res == nil { - return nil, RedisError("Key `" + name + "` does not exist") +func (client *Client) Auth(password string) os.Error { + _, err := client.sendCommand("AUTH", []string{password}) + if err != nil { + return err } - data := res.([]byte) - return data, nil + return nil } -func (client *Client) Mget(keys []string) ([][]byte, os.Error) { - cmd := fmt.Sprintf("MGET %s\r\n", strings.Join(keys, " ")) - res, err := client.sendCommand(cmd) +func (client *Client) Exists(key string) (bool, os.Error) { + cmd := fmt.Sprintf("EXISTS %s\r\n", key) + res, err := client.sendCommand(cmd, []string{key}) if err != nil { - return nil, err + return false, err } - - data := res.([][]byte) - return data, nil + return res.(int64) == 1, nil } -func (client *Client) Set(key string, val []byte) os.Error { - cmd := fmt.Sprintf("SET %s %d\r\n%s\r\n", key, len(val), val) - _, err := client.sendCommand(cmd) +func (client *Client) Del(key string) (bool, os.Error) { + res, err := client.sendCommand("DEL", []string{key}) if err != nil { - return err + return false, err } - return nil + return res.(int64) == 1, nil } -func (client *Client) Setnx(key string, val []byte) os.Error { - cmd := fmt.Sprintf("SETNX %s %d\r\n%s\r\n", key, len(val), val) - _, err := client.sendCommand(cmd) +func (client *Client) Type(key string) (string, os.Error) { + res, err := client.sendCommand("TYPE", []string{key}) if err != nil { - return err + return "", err } - return nil + return res.(string), nil } -func (client *Client) Getset(key string, val []byte) ([]byte, os.Error) { - cmd := fmt.Sprintf("GETSET %s %d\r\n%s\r\n", key, len(val), val) - res, err := client.sendCommand(cmd) +func (client *Client) Keys(pattern string) ([]string, os.Error) { + res, err := client.sendCommand("KEYS", []string{pattern}) if err != nil { return nil, err } - data := res.([]byte) - return data, nil + keys := bytes.Fields(res.([]byte)) + ret := make([]string, len(keys)) + for i, k := range keys { + ret[i] = string(k) + } + return ret, nil +} + +func (client *Client) Randomkey() (string, os.Error) { + res, err := client.sendCommand("RANDOMKEY", nil) + if err != nil { + return "", err + } + return res.(string), nil } -func (client *Client) Del(name string) (bool, os.Error) { - cmd := fmt.Sprintf("DEL %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Rename(src string, dst string) os.Error { + _, err := client.sendCommand("RENAME", []string{src, dst}) + if err != nil { + return err + } + return nil +} + +func (client *Client) Renamenx(src string, dst string) (bool, os.Error) { + res, err := client.sendCommand("RENAMENX", []string{src, dst}) if err != nil { return false, err } + return res.(int64) == 1, nil +} + +func (client *Client) Dbsize() (int, os.Error) { + res, err := client.sendCommand("DBSIZE", nil) + if err != nil { + return -1, err + } - return res.(int) == 1, nil + return int(res.(int64)), nil } -func (client *Client) Keys(pattern string) ([]string, os.Error) { - cmd := fmt.Sprintf("KEYS %s\r\n", pattern) - res, err := client.sendCommand(cmd) +func (client *Client) Expire(key string, time int64) (bool, os.Error) { + res, err := client.sendCommand("EXPIRE", []string{key, strconv.Itoa64(time)}) if err != nil { - return nil, err + return false, err } - keys := bytes.Fields(res.([]byte)) - var ret vector.StringVector - for _, k := range keys { - ret.Push(string(k)) + return res.(int64) == 1, nil +} + +func (client *Client) Ttl(key string) (int64, os.Error) { + res, err := client.sendCommand("TTL", []string{key}) + if err != nil { + return -1, err } - return ret, nil + + return res.(int64), nil } -func (client *Client) Type(key string) (string, os.Error) { - cmd := fmt.Sprintf("TYPE %s\r\n", key) - res, err := client.sendCommand(cmd) +func (client *Client) Move(key string, dbnum int) (bool, os.Error) { + res, err := client.sendCommand("MOVE", []string{key, strconv.Itoa(dbnum)}) if err != nil { - return "", err + return false, err } - return res.(string), nil + return res.(int64) == 1, nil } -func (client *Client) Auth(password string) os.Error { - cmd := fmt.Sprintf("AUTH %s\r\n", password) - _, err := client.sendCommand(cmd) +func (client *Client) Flush(all bool) os.Error { + var cmd string + if all { + cmd = "FLUSHALL" + } else { + cmd = "FLUSHDB" + } + _, err := client.sendCommand(cmd, nil) + if err != nil { + return err + } + return nil +} + +// String-related commands + +func (client *Client) Set(key string, val []byte) os.Error { + _, err := client.sendCommand("SET", []string{key, string(val)}) + if err != nil { return err } @@ -310,45 +354,90 @@ func (client *Client) Auth(password string) os.Error { return nil } -func (client *Client) Exists(key string) (bool, os.Error) { - cmd := fmt.Sprintf("EXISTS %s\r\n", key) - res, err := client.sendCommand(cmd) +func (client *Client) Get(key string) ([]byte, os.Error) { + res, _ := client.sendCommand("GET", []string{key}) + + if res == nil { + return nil, RedisError("Key `" + key + "` does not exist") + } + + data := res.([]byte) + return data, nil +} + +func (client *Client) Getset(key string, val []byte) ([]byte, os.Error) { + res, err := client.sendCommand("GETSET", []string{key, string(val)}) + if err != nil { - return false, err + return nil, err } - return res.(int) == 1, nil + + data := res.([]byte) + return data, nil } -func (client *Client) Rename(src string, dst string) os.Error { - cmd := fmt.Sprintf("RENAME %s %s\r\n", src, dst) +func (client *Client) Mget(keys []string) ([][]byte, os.Error) { + res, err := client.sendCommand("MGET", keys) + if err != nil { + return nil, err + } + + data := res.([][]byte) + return data, nil +} + +func (client *Client) Setnx(key string, val []byte) os.Error { + _, err := client.sendCommand("SETNX", []string{key, string(val)}) - _, err := client.sendCommand(cmd) if err != nil { return err } + return nil } -func (client *Client) Renamenx(src string, dst string) (bool, os.Error) { - cmd := fmt.Sprintf("RENAMENX %s %s\r\n", src, dst) +func (client *Client) Setex(key string, time int64, val []byte) os.Error { + _, err := client.sendCommand("SETEX", []string{key, strconv.Itoa64(time), string(val)}) - res, err := client.sendCommand(cmd) if err != nil { - return false, err + return err } - return res.(int) == 1, nil + + return nil } -func (client *Client) Randomkey() (string, os.Error) { - res, err := client.sendCommand("RANDOMKEY\r\n") +func (client *Client) Mset(mapping map[string][]byte) os.Error { + args := make([]string, len(mapping)*2) + i := 0 + for k, v := range mapping { + args[i] = k + args[i+1] = string(v) + i += 2 + } + _, err := client.sendCommand("MSET", args) if err != nil { - return "", err + return err } - return res.(string), nil + return nil } -func (client *Client) Dbsize() (int64, os.Error) { - res, err := client.sendCommand("DBSIZE\r\n") +func (client *Client) Msetnx(mapping map[string][]byte) os.Error { + args := make([]string, len(mapping)*2) + i := 0 + for k, v := range mapping { + args[i] = k + args[i+1] = string(v) + i += 2 + } + _, err := client.sendCommand("MSETNX", args) + if err != nil { + return err + } + return nil +} + +func (client *Client) Incr(key string) (int64, os.Error) { + res, err := client.sendCommand("INCR", []string{key}) if err != nil { return -1, err } @@ -356,20 +445,26 @@ func (client *Client) Dbsize() (int64, os.Error) { return res.(int64), nil } -func (client *Client) Expire(key string, time int64) (bool, os.Error) { - cmd := fmt.Sprintf("EXPIRE %s %d\r\n", key, time) - res, err := client.sendCommand(cmd) +func (client *Client) Incrby(key string, val int64) (int64, os.Error) { + res, err := client.sendCommand("INCRBY", []string{key, strconv.Itoa64(val)}) + if err != nil { + return -1, err + } + + return res.(int64), nil +} +func (client *Client) Decr(key string) (int64, os.Error) { + res, err := client.sendCommand("DECR", []string{key}) if err != nil { - return false, err + return -1, err } - return res.(int64) == 1, nil + return res.(int64), nil } -func (client *Client) Ttl(key string) (int64, os.Error) { - cmd := fmt.Sprintf("TTL %s\r\n", key) - res, err := client.sendCommand(cmd) +func (client *Client) Decrby(key string, val int64) (int64, os.Error) { + res, err := client.sendCommand("DECRBY", []string{key, strconv.Itoa64(val)}) if err != nil { return -1, err } @@ -377,44 +472,60 @@ func (client *Client) Ttl(key string) (int64, os.Error) { return res.(int64), nil } -func (client *Client) Move(key string, dbnum int) (bool, os.Error) { - cmd := fmt.Sprintf("MOVE %s %d\r\n", key, dbnum) - res, err := client.sendCommand(cmd) +func (client *Client) Append(key string, val []byte) os.Error { + _, err := client.sendCommand("APPEND", []string{key, string(val)}) if err != nil { - return false, err + return err } - return res.(int) == 1, nil + return nil } -func (client *Client) Flush(all bool) os.Error { - var cmd string - if all { - cmd = "FLUSHALL\r\n" - } else { - cmd = "FLUSHDB\r\n" +func (client *Client) Substr(key string, start int, end int) ([]byte, os.Error) { + res, _ := client.sendCommand("SUBSTR", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) + + if res == nil { + return nil, RedisError("Key `" + key + "` does not exist") } - _, err := client.sendCommand(cmd) + + data := res.([]byte) + return data, nil +} + +// List commands + +func (client *Client) Rpush(key string, val []byte) os.Error { + _, err := client.sendCommand("RPUSH", []string{key, string(val)}) + + if err != nil { + return err + } + + return nil +} + +func (client *Client) Lpush(key string, val []byte) os.Error { + _, err := client.sendCommand("LPUSH", []string{key, string(val)}) + if err != nil { return err } + return nil } -func (client *Client) Llen(name string) (int, os.Error) { - cmd := fmt.Sprintf("LLEN %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Llen(key string) (int, os.Error) { + res, err := client.sendCommand("LLEN", []string{key}) if err != nil { return -1, err } - return res.(int), nil + return int(res.(int64)), nil } -func (client *Client) Lrange(name string, start int, end int) ([][]byte, os.Error) { - cmd := fmt.Sprintf("LRANGE %s %d %d\r\n", name, start, end) - res, err := client.sendCommand(cmd) +func (client *Client) Lrange(key string, start int, end int) ([][]byte, os.Error) { + res, err := client.sendCommand("LRANGE", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) if err != nil { return nil, err } @@ -422,9 +533,18 @@ func (client *Client) Lrange(name string, start int, end int) ([][]byte, os.Erro return res.([][]byte), nil } -func (client *Client) Lindex(name string, index int) ([]byte, os.Error) { - cmd := fmt.Sprintf("LINDEX %s %d\r\n", name, index) - res, err := client.sendCommand(cmd) +func (client *Client) Ltrim(key string, start int, end int) os.Error { + _, err := client.sendCommand("LTRIM", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) + + if err != nil { + return err + } + + return nil +} + +func (client *Client) Lindex(key string, index int) ([]byte, os.Error) { + res, err := client.sendCommand("LINDEX", []string{key, strconv.Itoa(index)}) if err != nil { return nil, err @@ -433,9 +553,8 @@ func (client *Client) Lindex(name string, index int) ([]byte, os.Error) { return res.([]byte), nil } -func (client *Client) Lset(name string, index int, value []byte) os.Error { - cmd := fmt.Sprintf("LSET %s %d %d\r\n%s\r\n", name, index, len(value), value) - _, err := client.sendCommand(cmd) +func (client *Client) Lset(key string, index int, value []byte) os.Error { + _, err := client.sendCommand("LSET", []string{key, strconv.Itoa(index), string(value)}) if err != nil { return err } @@ -443,19 +562,17 @@ func (client *Client) Lset(name string, index int, value []byte) os.Error { return nil } -func (client *Client) Lrem(name string, index int) (int, os.Error) { - cmd := fmt.Sprintf("LREM %s %d\r\n", name, index) - res, err := client.sendCommand(cmd) +func (client *Client) Lrem(key string, index int) (int, os.Error) { + res, err := client.sendCommand("LREM", []string{key, strconv.Itoa(index)}) if err != nil { return -1, err } - return res.(int), nil + return int(res.(int64)), nil } -func (client *Client) Lpop(name string) ([]byte, os.Error) { - cmd := fmt.Sprintf("LPOP %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Lpop(key string) ([]byte, os.Error) { + res, err := client.sendCommand("LPOP", []string{key}) if err != nil { return nil, err } @@ -463,9 +580,8 @@ func (client *Client) Lpop(name string) ([]byte, os.Error) { return res.([]byte), nil } -func (client *Client) Rpop(name string) ([]byte, os.Error) { - cmd := fmt.Sprintf("RPOP %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Rpop(key string) ([]byte, os.Error) { + res, err := client.sendCommand("RPOP", []string{key}) if err != nil { return nil, err } @@ -473,9 +589,8 @@ func (client *Client) Rpop(name string) ([]byte, os.Error) { return res.([]byte), nil } -func (client *Client) Blpop(name string) ([]byte, os.Error) { - cmd := fmt.Sprintf("BLPOP %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Blpop(key string) ([]byte, os.Error) { + res, err := client.sendCommand("BLPOP", []string{key}) if err != nil { return nil, err } @@ -483,9 +598,8 @@ func (client *Client) Blpop(name string) ([]byte, os.Error) { return res.([]byte), nil } -func (client *Client) Brpop(name string) ([]byte, os.Error) { - cmd := fmt.Sprintf("BRPOP %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Brpop(key string) ([]byte, os.Error) { + res, err := client.sendCommand("BRPOP", []string{key}) if err != nil { return nil, err } @@ -494,8 +608,7 @@ func (client *Client) Brpop(name string) ([]byte, os.Error) { } func (client *Client) Rpoplpush(src string, dst string) ([]byte, os.Error) { - cmd := fmt.Sprintf("RPOPLPUSH %s %s\r\n", src, dst) - res, err := client.sendCommand(cmd) + res, err := client.sendCommand("RPOPLPUSH", []string{src, dst}) if err != nil { return nil, err } @@ -503,54 +616,117 @@ func (client *Client) Rpoplpush(src string, dst string) ([]byte, os.Error) { return res.([]byte), nil } -func (client *Client) Rpush(name string, value []byte) os.Error { - cmd := fmt.Sprintf("RPUSH %s %d\r\n%s\r\n", name, len(value), value) - _, err := client.sendCommand(cmd) +// Set commands + +func (client *Client) Sadd(key string, value []byte) (bool, os.Error) { + res, err := client.sendCommand("SADD", []string{key, string(value)}) if err != nil { - return err + return false, err } - return nil + return res.(int64) == 1, nil } -func (client *Client) Lpush(name string, value []byte) os.Error { - cmd := fmt.Sprintf("LPUSH %s %d\r\n%s\r\n", name, len(value), value) - _, err := client.sendCommand(cmd) +func (client *Client) Srem(key string, value []byte) (bool, os.Error) { + res, err := client.sendCommand("SREM", []string{key, string(value)}) if err != nil { - return err + return false, err } - return nil + return res.(int64) == 1, nil } -func (client *Client) Ltrim(name string, start int64, end int64) os.Error { - cmd := fmt.Sprintf("LTRIM %s %d %d\r\n", name, start, end) - _, err := client.sendCommand(cmd) +func (client *Client) Spop(key string) ([]byte, os.Error) { + res, err := client.sendCommand("SPOP", []string{key}) + if err != nil { + return nil, err + } + + if res == nil { + return nil, RedisError("Spop failed") + } + data := res.([]byte) + return data, nil +} + +func (client *Client) Smove(src string, dst string, val []byte) (bool, os.Error) { + res, err := client.sendCommand("SMOVE", []string{src, dst, string(val)}) if err != nil { - return err + return false, err } - return nil + return res.(int64) == 1, nil } -func (client *Client) Sismember(name string, value []byte) (bool, os.Error) { - cmd := fmt.Sprintf("SISMEMBER %s %d\r\n%s\r\n", name, len(value), value) - res, err := client.sendCommand(cmd) +func (client *Client) Scard(key string) (int, os.Error) { + res, err := client.sendCommand("SCARD", []string{key}) + if err != nil { + return -1, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Sismember(key string, value []byte) (bool, os.Error) { + res, err := client.sendCommand("SISMEMBER", []string{key, string(value)}) if err != nil { return false, err } - return res.(int) == 1, nil + return res.(int64) == 1, nil +} + +func (client *Client) Sinter(keys []string) ([][]byte, os.Error) { + res, err := client.sendCommand("SINTER", keys) + if err != nil { + return nil, err + } + + return res.([][]byte), nil } -func (client *Client) Smembers(name string) ([][]byte, os.Error) { - cmd := fmt.Sprintf("SMEMBERS %s\r\n", name) - res, err := client.sendCommand(cmd) +func (client *Client) Sinterstore(dst string, keys []string) (int, os.Error) { + args := make([]string, len(keys)+1) + args[0] = dst + copy(args[1:], keys) + res, err := client.sendCommand("SINTERSTORE", args) + if err != nil { + return 0, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Sunion(keys []string) ([][]byte, os.Error) { + res, err := client.sendCommand("SUNION", keys) + if err != nil { + return nil, err + } + + return res.([][]byte), nil +} + +func (client *Client) Sunionstore(dst string, keys []string) (int, os.Error) { + args := make([]string, len(keys)+1) + args[0] = dst + copy(args[1:], keys) + res, err := client.sendCommand("SUNIONSTORE", args) + if err != nil { + return 0, err + } + + return int(res.(int64)), nil +} +func (client *Client) Sdiff(key1 string, keys []string) ([][]byte, os.Error) { + args := make([]string, len(keys)+1) + args[0] = key1 + copy(args[1:], keys) + res, err := client.sendCommand("SDIFF", args) if err != nil { return nil, err } @@ -558,24 +734,297 @@ func (client *Client) Smembers(name string) ([][]byte, os.Error) { return res.([][]byte), nil } -func (client *Client) Sadd(name string, value []byte) (bool, os.Error) { - cmd := fmt.Sprintf("SADD %s %d\r\n%s\r\n", name, len(value), value) - res, err := client.sendCommand(cmd) +func (client *Client) Sdiffstore(dst string, key1 string, keys []string) (int, os.Error) { + args := make([]string, len(keys)+2) + args[0] = dst + args[1] = key1 + copy(args[2:], keys) + res, err := client.sendCommand("SDIFFSTORE", args) + if err != nil { + return 0, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Smembers(key string) ([][]byte, os.Error) { + res, err := client.sendCommand("SMEMBERS", []string{key}) + + if err != nil { + return nil, err + } + + return res.([][]byte), nil +} + +func (client *Client) Srandmember(key string) ([]byte, os.Error) { + res, err := client.sendCommand("SRANDMEMBER", []string{key}) + if err != nil { + return nil, err + } + + return res.([]byte), nil +} + +// sorted set commands + +func (client *Client) Zadd(key string, value []byte, score float64) (bool, os.Error) { + res, err := client.sendCommand("ZADD", []string{key, string(value), strconv.Ftoa64(score, 'f', -1)}) + if err != nil { + return false, err + } + + return res.(int64) == 1, nil +} + +func (client *Client) Zrem(key string, value []byte) (bool, os.Error) { + res, err := client.sendCommand("ZREM", []string{key, string(value)}) + if err != nil { + return false, err + } + + return res.(int64) == 1, nil +} + +func (client *Client) Zincrby(key string, value []byte, score float64) (float64, os.Error) { + res, err := client.sendCommand("ZINCRBY", []string{key, string(value), strconv.Ftoa64(score, 'f', -1)}) + if err != nil { + return 0, err + } + + data := string(res.([]byte)) + f, _ := strconv.Atof64(data) + return f, nil +} + +func (client *Client) Zrank(key string, value []byte) (int, os.Error) { + res, err := client.sendCommand("ZRANK", []string{key, string(value)}) + if err != nil { + return 0, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Zrevrank(key string, value []byte) (int, os.Error) { + res, err := client.sendCommand("ZREVRANK", []string{key, string(value)}) + if err != nil { + return 0, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Zrange(key string, start int, end int) ([][]byte, os.Error) { + res, err := client.sendCommand("ZRANGE", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) + if err != nil { + return nil, err + } + + return res.([][]byte), nil +} + +func (client *Client) Zrevrange(key string, start int, end int) ([][]byte, os.Error) { + res, err := client.sendCommand("ZREVRANGE", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) + if err != nil { + return nil, err + } + + return res.([][]byte), nil +} + +func (client *Client) Zrangebyscore(key string, start float64, end float64) ([][]byte, os.Error) { + res, err := client.sendCommand("ZRANGEBYSCORE", []string{key, strconv.Ftoa64(start, 'f', -1), strconv.Ftoa64(end, 'f', -1)}) + if err != nil { + return nil, err + } + + return res.([][]byte), nil +} + +func (client *Client) Zcard(key string) (int, os.Error) { + res, err := client.sendCommand("ZCARD", []string{key}) + if err != nil { + return -1, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Zscore(key string) (float64, os.Error) { + res, err := client.sendCommand("ZSCORE", []string{key}) + if err != nil { + return 0, err + } + + data := string(res.([]byte)) + f, _ := strconv.Atof64(data) + return f, nil +} + +func (client *Client) Zremrangebyrank(key string, start int, end int) (int, os.Error) { + res, err := client.sendCommand("ZREMRANGEBYRANK", []string{key, strconv.Itoa(start), strconv.Itoa(end)}) + if err != nil { + return -1, err + } + + return int(res.(int64)), nil +} +func (client *Client) Zremrangebyscore(key string, start float64, end float64) (int, os.Error) { + res, err := client.sendCommand("ZREMRANGEBYSCORE", []string{key, strconv.Ftoa64(start, 'f', -1), strconv.Ftoa64(end, 'f', -1)}) + if err != nil { + return -1, err + } + + return int(res.(int64)), nil +} + +// hash commands + +func (client *Client) Hset(key string, field string, val []byte) (bool, os.Error) { + res, err := client.sendCommand("HSET", []string{key, field, string(val)}) if err != nil { return false, err } - return res.(int) == 1, nil + return res.(int64) == 1, nil } -func (client *Client) Srem(name string, value []byte) (bool, os.Error) { - cmd := fmt.Sprintf("SREM %s %d\r\n%s\r\n", name, len(value), value) - res, err := client.sendCommand(cmd) +func (client *Client) Hget(key string, field string) ([]byte, os.Error) { + res, _ := client.sendCommand("HGET", []string{key, field}) + + if res == nil { + return nil, RedisError("Hget failed") + } + + data := res.([]byte) + return data, nil +} + +func (client *Client) Hmset(key string, mapping map[string][]byte) os.Error { + + args := make([]string, len(mapping)*2+1) + args[0] = key + i := 1 + for k, v := range mapping { + args[i] = k + args[i+1] = string(v) + i += 2 + } + _, err := client.sendCommand("HMSET", args) + if err != nil { + return err + } + return nil +} + +func (client *Client) Hincrby(key string, field string, val int64) (int64, os.Error) { + res, err := client.sendCommand("HINCRBY", []string{key, field, strconv.Itoa64(val)}) + if err != nil { + return -1, err + } + + return res.(int64), nil +} + +func (client *Client) Hexists(key string, field string) (bool, os.Error) { + res, err := client.sendCommand("HEXISTS", []string{key, field}) + if err != nil { + return false, err + } + return res.(int64) == 1, nil +} + +func (client *Client) Hdel(key string, field string) (bool, os.Error) { + res, err := client.sendCommand("HDEL", []string{key, field}) if err != nil { return false, err } - return res.(int) == 1, nil + return res.(int64) == 1, nil +} + +func (client *Client) Hlen(key string) (int, os.Error) { + res, err := client.sendCommand("HLEN", []string{key}) + if err != nil { + return -1, err + } + + return int(res.(int64)), nil +} + +func (client *Client) Hkeys(key string) ([]string, os.Error) { + res, err := client.sendCommand("HKEYS", []string{key}) + + if err != nil { + return nil, err + } + + data := res.([][]byte) + ret := make([]string, len(data)) + for i, k := range data { + ret[i] = string(k) + } + return ret, nil +} + +func (client *Client) Hvals(key string) ([][]byte, os.Error) { + res, err := client.sendCommand("HVALS", []string{key}) + + if err != nil { + return nil, err + } + return res.([][]byte), nil +} + +func (client *Client) Hgetall(key string) (map[string][]byte, os.Error) { + res, err := client.sendCommand("HGETALL", []string{key}) + + if err != nil { + return nil, err + } + data := res.([][]byte) + ret := make(map[string][]byte, len(data)/2) + for i := 0; i < len(data)/2; i++ { + ret[string(data[i*2])] = data[i*2+1] + } + + return ret, nil +} + +//Server commands + +func (client *Client) Save() os.Error { + _, err := client.sendCommand("SAVE", nil) + if err != nil { + return err + } + return nil +} + +func (client *Client) Bgsave() os.Error { + _, err := client.sendCommand("BGSAVE", nil) + if err != nil { + return err + } + return nil +} + +func (client *Client) Lastsave() (int64, os.Error) { + res, err := client.sendCommand("LASTSAVE", nil) + if err != nil { + return 0, err + } + + return res.(int64), nil +} + +func (client *Client) Bgrewriteaof() os.Error { + _, err := client.sendCommand("BGREWRITEAOF", nil) + if err != nil { + return err + } + return nil } diff --git a/redis_test.go b/redis_test.go index d4261e7..863590c 100644 --- a/redis_test.go +++ b/redis_test.go @@ -2,8 +2,10 @@ package redis import ( "os" + "reflect" "runtime" "strconv" + "strings" "testing" ) @@ -17,7 +19,7 @@ var client Client func init() { runtime.GOMAXPROCS(2) - client.Addr = "127.0.0.1:7379" + client.Addr = "127.0.0.1:8379" client.Db = 13 } @@ -158,6 +160,78 @@ func TestList(t *testing.T) { } +func verifyHash(t *testing.T, key string, expected map[string][]byte) { + //test Hget + m1 := make(map[string][]byte) + for k, _ := range expected { + actual, err := client.Hget("h", k) + if err != nil { + t.Fatal("verifyHash Hget failed", err.String()) + } + m1[k] = actual + } + if !reflect.DeepEqual(m1, expected) { + t.Fatal("verifyHash Hget failed") + } + + + //test Hkeys + keys, err := client.Hkeys(key) + if err != nil { + t.Fatal("verifyHash Hkeys failed", err.String()) + } + if len(keys) != len(expected) { + t.Fatal("verifyHash Hkeys failed") + } + for _, key := range keys { + if expected[key] == nil { + t.Fatal("verifyHash Hkeys failed") + } + } + + //test Hvals + vals, err := client.Hvals(key) + if err != nil { + t.Fatal("verifyHash Hvals failed", err.String()) + } + if len(vals) != len(expected) { + t.Fatal("verifyHash Hvals failed") + } + + //test Hgetall + m2, err := client.Hgetall(key) + if err != nil { + t.Fatal("verifyHash Hgetall failed", err.String()) + } + if !reflect.DeepEqual(m2, expected) { + t.Fatal("verifyHash Hgetall failed") + } +} + +func TestHash(t *testing.T) { + //test cast + keys := []string{"a", "b", "c", "d", "e"} + test := make(map[string][]byte) + for _, v := range keys { + test[v] = []byte(strings.Repeat(v, 5)) + } + + //set with hset + for k, v := range test { + client.Hset("h", k, v) + } + //test hset + verifyHash(t, "h", test) + + //set with hmset + client.Hmset("h2", test) + //test hset + verifyHash(t, "h2", test) + + client.Del("h") + client.Del("h2") +} + /* func TestTimeout(t *testing.T) { client.Set("a", []byte("hello world"))