diff --git a/server/http/base.go b/server/http/base.go index 4a05cb4..6b1781d 100644 --- a/server/http/base.go +++ b/server/http/base.go @@ -7,9 +7,15 @@ import ( "strings" ) -const ERR_ARGUMENT_FORMAT = "ERR wrong number of arguments for '%s' command" +const ( + ERR_ARGUMENT_FORMAT = "ERR wrong number of arguments for '%s' command" + MSG_OK = "OK" +) -var ErrValue = errors.New("ERR value is not an integer or out of range") +var ( + ErrValue = errors.New("ERR value is not an integer or out of range") + ErrSyntax = errors.New("ERR syntax error") +) type commondFunc func(*ledis.DB, ...string) (interface{}, error) diff --git a/server/http/cmd_hash.go b/server/http/cmd_hash.go new file mode 100644 index 0000000..cf0955b --- /dev/null +++ b/server/http/cmd_hash.go @@ -0,0 +1,306 @@ +package http + +import ( + "fmt" + "github.com/siddontang/ledisdb/ledis" + "strconv" +) + +func hsetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hset") + } + + key := []byte(args[0]) + field := []byte(args[1]) + value := []byte(args[2]) + if n, err := db.HSet(key, field, value); err != nil { + return nil, err + } else { + return n, err + } +} + +func hgetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hget") + } + + key := []byte(args[0]) + field := []byte(args[1]) + + if v, err := db.HGet(key, field); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func hexistsCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hexists") + } + key := []byte(args[0]) + field := []byte(args[1]) + + var n int64 = 1 + if v, err := db.HGet(key, field); err != nil { + return nil, err + } else { + if v == nil { + n = 0 + } + return n, nil + } +} + +func hdelCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hdel") + } + key := []byte(args[0]) + fields := make([][]byte, len(args[1:])) + for i, arg := range args[1:] { + fields[i] = []byte(arg) + } + + if n, err := db.HDel(key, fields...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func hlenCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hlen") + } + key := []byte(args[0]) + if n, err := db.HLen(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func hincrbyCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hincrby") + } + key := []byte(args[0]) + field := []byte(args[1]) + delta, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return nil, ErrValue + } + + var n int64 + if n, err = db.HIncrBy(key, field, delta); err != nil { + return nil, err + } else { + return n, nil + } +} + +func hmsetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hmset") + } + + if len(args[1:])%2 != 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hmset") + } + key := []byte(args[0]) + args = args[1:] + kvs := make([]ledis.FVPair, len(args)/2) + for i := 0; i < len(kvs); i++ { + kvs[i].Field = []byte(args[2*i]) + kvs[i].Value = []byte(args[2*i+1]) + } + if err := db.HMset(key, kvs...); err != nil { + return nil, err + } else { + return []interface{}{true, MSG_OK}, nil + } +} + +func hmgetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hmget") + } + key := []byte(args[0]) + fields := make([][]byte, len(args[1:])) + for i, arg := range args[1:] { + fields[i] = []byte(arg) + } + if vals, err := db.HMget(key, fields...); err != nil { + return nil, err + } else { + arr := make([]interface{}, len(vals)) + for i, v := range vals { + if v == nil { + arr[i] = nil + } else { + arr[i] = ledis.String(v) + } + } + return arr, nil + } +} + +func hgetallCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hgetall") + } + key := []byte(args[0]) + if fvs, err := db.HGetAll(key); err != nil { + return nil, err + } else { + var m = make(map[string]string) + for _, fv := range fvs { + m[ledis.String(fv.Field)] = ledis.String(fv.Value) + } + return m, nil + } +} + +func hkeysCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hkeys") + } + key := []byte(args[0]) + if v, err := db.HKeys(key); err != nil { + return nil, err + } else { + return v, nil + } +} + +func hvalsCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hvals") + } + key := []byte(args[0]) + if vals, err := db.HValues(key); err != nil { + return nil, err + } else { + var arr = make([]string, len(vals)) + for i, v := range vals { + arr[i] = ledis.String(v) + } + return arr, nil + } +} + +func hclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hclear") + } + key := []byte(args[0]) + if n, err := db.HClear(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func hmclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hmclear") + } + keys := make([][]byte, len(args)) + for i, arg := range args { + keys[i] = []byte(arg) + } + + if n, err := db.HMclear(keys...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func hexpireCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hexpire") + } + key := []byte(args[0]) + duration, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + if v, err := db.HExpire(key, duration); err != nil { + return nil, err + } else { + return v, nil + } +} + +func hexpireAtCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hexpireat") + } + key := []byte(args[0]) + + when, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + if v, err := db.HExpireAt(key, when); err != nil { + return nil, err + } else { + return v, nil + } +} + +func httlCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "httl") + } + + key := []byte(args[0]) + if v, err := db.HTTL(key); err != nil { + return nil, err + } else { + return v, nil + } +} + +func hpersistCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "hpersist") + } + key := []byte(args[0]) + if n, err := db.HPersist(key); err != nil { + return nil, err + } else { + return n, err + } +} + +func init() { + register("hdel", hdelCommand) + register("hexists", hexistsCommand) + register("hget", hgetCommand) + register("hgetall", hgetallCommand) + register("hincrby", hincrbyCommand) + register("hkeys", hkeysCommand) + register("hlen", hlenCommand) + register("hmget", hmgetCommand) + register("hmset", hmsetCommand) + register("hset", hsetCommand) + register("hvals", hvalsCommand) + + //ledisdb special command + + register("hclear", hclearCommand) + register("hmclear", hmclearCommand) + register("hexpire", hexpireCommand) + register("hexpireat", hexpireAtCommand) + register("httl", httlCommand) + register("hpersist", hpersistCommand) +} diff --git a/server/http/cmd_kv.go b/server/http/cmd_kv.go new file mode 100644 index 0000000..dbd1116 --- /dev/null +++ b/server/http/cmd_kv.go @@ -0,0 +1,278 @@ +package http + +import ( + "fmt" + "github.com/siddontang/ledisdb/ledis" + "strconv" +) + +func getCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "get") + } + key := []byte(args[0]) + if v, err := db.Get(key); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func setCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "set") + } + + key := []byte(args[0]) + val := []byte(args[1]) + if err := db.Set(key, val); err != nil { + return nil, err + } else { + return []interface{}{true, MSG_OK}, nil + } + +} + +func getsetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "getset") + } + + key := []byte(args[0]) + val := []byte(args[1]) + if v, err := db.GetSet(key, val); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func setnxCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "setnx") + } + + key := []byte(args[0]) + val := []byte(args[1]) + if n, err := db.SetNX(key, val); err != nil { + return nil, err + } else { + return n, nil + } +} + +func existsCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "exists") + } + + if n, err := db.Exists([]byte(args[0])); err != nil { + return nil, err + } else { + return n, nil + } +} + +func incrCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "incr") + } + + key := []byte(args[0]) + if n, err := db.Incr(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func decrCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "decr") + } + + key := []byte(args[0]) + if n, err := db.Decr(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func incrbyCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "incrby") + } + + delta, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + + if n, err := db.IncryBy(key, delta); err != nil { + return nil, err + } else { + return n, nil + } +} + +func decrbyCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "decrby") + } + + delta, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + + if n, err := db.DecrBy(key, delta); err != nil { + return nil, err + } else { + return n, nil + } +} + +func delCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) == 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "del") + } + + keys := make([][]byte, len(args)) + if n, err := db.Del(keys...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func msetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) == 0 || len(args)%2 != 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "mset") + } + + kvs := make([]ledis.KVPair, len(args)/2) + for i := 0; i < len(kvs); i++ { + kvs[i].Key = []byte(args[2*i]) + kvs[i].Value = []byte(args[2*i+1]) + } + + if err := db.MSet(kvs...); err != nil { + return nil, err + } else { + return []interface{}{true, MSG_OK}, nil + } +} + +func mgetCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) == 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "mget") + } + + keys := make([][]byte, len(args)) + for i, arg := range args { + keys[i] = []byte(arg) + } + if vals, err := db.MGet(keys...); err != nil { + return nil, err + } else { + arr := make([]interface{}, len(vals)) + for i, v := range vals { + if v == nil { + arr[i] = nil + } else { + arr[i] = ledis.String(v) + } + } + return arr, nil + } +} + +func expireCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "expire") + } + + duration, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + key := []byte(args[0]) + if v, err := db.Expire(key, duration); err != nil { + return nil, err + } else { + return v, nil + } +} + +func expireAtCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "expireat") + } + + when, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + key := []byte(args[0]) + if v, err := db.ExpireAt(key, when); err != nil { + return nil, err + } else { + return v, nil + } +} + +func ttlCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "ttl") + } + key := []byte(args[0]) + + if v, err := db.TTL(key); err != nil { + return nil, err + } else { + return v, nil + } +} + +func persistCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "persist") + } + key := []byte(args[0]) + + if n, err := db.Persist(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func init() { + register("decr", decrCommand) + register("decrby", decrbyCommand) + register("del", delCommand) + register("exists", existsCommand) + register("get", getCommand) + register("getset", getsetCommand) + register("incr", incrCommand) + register("incrby", incrbyCommand) + register("mget", mgetCommand) + register("mset", msetCommand) + register("set", setCommand) + register("setnx", setnxCommand) + register("expire", expireCommand) + register("expireat", expireAtCommand) + register("ttl", ttlCommand) + register("persist", persistCommand) +} diff --git a/server/http/cmd_list.go b/server/http/cmd_list.go new file mode 100644 index 0000000..3f83b8e --- /dev/null +++ b/server/http/cmd_list.go @@ -0,0 +1,248 @@ +package http + +import ( + "fmt" + "github.com/siddontang/ledisdb/ledis" + "strconv" +) + +func lpushCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lpush") + } + key := []byte(args[0]) + elems := make([][]byte, len(args[1:])) + for i, arg := range args[1:] { + elems[i] = []byte(arg) + } + + if n, err := db.LPush(key, elems...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func rpushCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "rpush") + } + + key := []byte(args[0]) + elems := make([][]byte, len(args[1:])) + for i, arg := range args[1:] { + elems[i] = []byte(arg) + } + if n, err := db.RPush(key, elems...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func lpopCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lpop") + } + + key := []byte(args[0]) + + if v, err := db.LPop(key); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func rpopCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "rpop") + } + key := []byte(args[0]) + + if v, err := db.RPop(key); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func llenCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "llen") + } + + key := []byte(args[0]) + if n, err := db.LLen(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func lindexCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lindex") + } + + index, err := strconv.ParseInt(args[1], 10, 32) + if err != nil { + return nil, ErrValue + } + key := []byte(args[0]) + + if v, err := db.LIndex(key, int32(index)); err != nil { + return nil, err + } else { + if v == nil { + return nil, nil + } + return ledis.String(v), nil + } +} + +func lrangeCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lrange") + } + + start, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + stop, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + if vals, err := db.LRange(key, int32(start), int32(stop)); err != nil { + return nil, err + } else { + arr := make([]interface{}, len(vals)) + for i, v := range vals { + if v == nil { + arr[i] = nil + } else { + arr[i] = ledis.String(v) + } + } + return arr, nil + } +} + +func lclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lclear") + } + + key := []byte(args[0]) + if n, err := db.LClear(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func lmclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lmclear") + } + + keys := make([][]byte, len(args)) + for i, arg := range args { + keys[i] = []byte(arg) + } + if n, err := db.LMclear(keys...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func lexpireCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lexpire") + } + + duration, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + if v, err := db.LExpire(key, duration); err != nil { + return nil, err + } else { + return v, nil + } +} + +func lexpireAtCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lexpireat") + } + + when, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + if v, err := db.LExpireAt(key, when); err != nil { + return nil, err + } else { + return v, nil + } +} + +func lttlCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lttl") + } + + key := []byte(args[0]) + if v, err := db.LTTL(key); err != nil { + return nil, err + } else { + return v, nil + } +} + +func lpersistCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "lpersist") + } + key := []byte(args[0]) + if n, err := db.LPersist(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func init() { + register("lindex", lindexCommand) + register("llen", llenCommand) + register("lpop", lpopCommand) + register("lrange", lrangeCommand) + register("lpush", lpushCommand) + register("rpop", rpopCommand) + register("rpush", rpushCommand) + + //ledisdb special command + + register("lclear", lclearCommand) + register("lmclear", lmclearCommand) + register("lexpire", lexpireCommand) + register("lexpireat", lexpireAtCommand) + register("lttl", lttlCommand) + register("lpersist", lpersistCommand) +} diff --git a/server/http/cmd_zset.go b/server/http/cmd_zset.go new file mode 100644 index 0000000..f1ed15e --- /dev/null +++ b/server/http/cmd_zset.go @@ -0,0 +1,520 @@ +package http + +import ( + "errors" + "fmt" + "github.com/siddontang/ledisdb/ledis" + "math" + "strconv" + "strings" +) + +var errScoreOverflow = errors.New("zset score overflow") + +func zaddCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zadd") + } + + if len(args[1:])%2 != 0 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zadd") + } + + key := []byte(args[0]) + args = args[1:] + + params := make([]ledis.ScorePair, len(args)/2) + for i := 0; i < len(params); i++ { + score, err := strconv.ParseInt(args[2*i], 10, 64) + if err != nil { + return nil, ErrValue + } + + params[i].Score = score + params[i].Member = []byte(args[2*i+1]) + } + + if n, err := db.ZAdd(key, params...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zcardCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zcard") + } + + key := []byte(args[0]) + if n, err := db.ZCard(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zscoreCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zscore") + } + + key := []byte(args[0]) + member := []byte(args[1]) + + if s, err := db.ZScore(key, member); err != nil { + if err == ledis.ErrScoreMiss { + return nil, nil + } else { + return nil, err + } + } else { + return strconv.FormatInt(s, 10), nil + } +} + +func zremCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrem") + } + + key := []byte(args[0]) + members := make([][]byte, len(args[1:])) + for i, arg := range args[1:] { + members[i] = []byte(arg) + } + if n, err := db.ZRem(key, members...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zincrbyCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zincrby") + } + + key := []byte(args[0]) + + delta, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + member := []byte(args[2]) + if v, err := db.ZIncrBy(key, delta, member); err != nil { + return nil, err + } else { + return strconv.FormatInt(v, 10), nil + } +} + +func zparseScoreRange(minBuf string, maxBuf string) (min int64, max int64, err error) { + if strings.ToLower(minBuf) == "-inf" { + min = math.MinInt64 + } else { + var lopen bool = false + + if len(minBuf) == 0 { + err = ErrValue + return + } + + if minBuf[0] == '(' { + lopen = true + minBuf = minBuf[1:] + } + + min, err = strconv.ParseInt(minBuf, 10, 64) + if err != nil { + err = ErrValue + return + } + + if min <= ledis.MinScore || min >= ledis.MaxScore { + err = errScoreOverflow + return + } + + if lopen { + min++ + } + } + + if strings.ToLower(maxBuf) == "+inf" { + max = math.MaxInt64 + } else { + var ropen = false + + if len(maxBuf) == 0 { + err = ErrValue + return + } + + if maxBuf[0] == '(' { + ropen = true + maxBuf = maxBuf[1:] + } + + max, err = strconv.ParseInt(maxBuf, 10, 64) + if err != nil { + err = ErrValue + return + } + + if max <= ledis.MinScore || max >= ledis.MaxScore { + err = errScoreOverflow + return + } + + if ropen { + max-- + } + } + return +} + +func zcountCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zcount") + } + + min, max, err := zparseScoreRange(args[1], args[2]) + if err != nil { + return nil, err + } + + if min > max { + return 0, nil + } + + key := []byte(args[0]) + if n, err := db.ZCount(key, min, max); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zrankCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrank") + } + key := []byte(args[0]) + member := []byte(args[1]) + + if n, err := db.ZRank(key, member); err != nil { + return nil, err + } else if n == -1 { + return nil, nil + } else { + return n, nil + } +} + +func zrevrankCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrevrank") + } + + key := []byte(args[0]) + member := []byte(args[1]) + if n, err := db.ZRevRank(key, member); err != nil { + return nil, err + } else if n == -1 { + return nil, nil + } else { + return n, nil + } +} + +func zremrangebyrankCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zremrangebyrank") + } + + key := []byte(args[0]) + + start, err := strconv.Atoi(args[1]) + if err != nil { + return nil, ErrValue + } + stop, err := strconv.Atoi(args[2]) + + if err != nil { + return nil, ErrValue + } + + if n, err := db.ZRemRangeByRank(key, start, stop); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zremrangebyscoreCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zremrangebyscore") + } + + key := []byte(args[0]) + min, max, err := zparseScoreRange(args[1], args[2]) + if err != nil { + return nil, err + } + + if n, err := db.ZRemRangeByScore(key, min, max); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zrangeGeneric(db *ledis.DB, reverse bool, args ...string) (interface{}, error) { + + key := []byte(args[0]) + + start, err := strconv.Atoi(args[1]) + if err != nil { + return nil, ErrValue + } + + stop, err := strconv.Atoi(args[2]) + if err != nil { + return nil, ErrValue + } + + args = args[3:] + var withScores bool = false + + if len(args) > 0 && strings.ToLower(args[0]) == "withscores" { + withScores = true + } + + if datas, err := db.ZRangeGeneric(key, start, stop, reverse); err != nil { + return nil, err + } else { + return makeScorePairArray(datas, withScores), nil + } +} + +func makeScorePairArray(datas []ledis.ScorePair, withScores bool) []string { + var arr []string + if withScores { + arr = make([]string, 2*len(datas)) + for i, data := range datas { + arr[2*i] = ledis.String(data.Member) + arr[2*i+1] = strconv.FormatInt(data.Score, 10) + } + } else { + arr = make([]string, len(datas)) + for i, data := range datas { + arr[i] = ledis.String(data.Member) + } + } + return arr +} + +func zrangeCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrange") + } + return zrangeGeneric(db, false, args...) +} + +func zrevrangeCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrevrange") + } + return zrangeGeneric(db, true, args...) +} + +func zrangebyscoreGeneric(db *ledis.DB, reverse bool, args ...string) (interface{}, error) { + key := []byte(args[0]) + + var minScore, maxScore string + + if !reverse { + minScore, maxScore = args[1], args[2] + } else { + minScore, maxScore = args[2], args[1] + } + + min, max, err := zparseScoreRange(minScore, maxScore) + + if err != nil { + return nil, err + } + + args = args[3:] + + var withScores bool = false + + if len(args) > 0 && strings.ToLower(args[0]) == "withscores" { + withScores = true + args = args[1:] + } + + var offset int = 0 + var count int = -1 + + if len(args) > 0 { + if len(args) != 3 { + return nil, ErrSyntax + } + + if strings.ToLower(args[0]) != "limit" { + return nil, ErrSyntax + } + + if offset, err = strconv.Atoi(args[1]); err != nil { + return nil, ErrValue + } + + if count, err = strconv.Atoi(args[2]); err != nil { + return nil, ErrValue + } + } + + if offset < 0 { + return []interface{}{}, nil + } + + if datas, err := db.ZRangeByScoreGeneric(key, min, max, offset, count, reverse); err != nil { + return nil, err + } else { + return makeScorePairArray(datas, withScores), nil + } +} + +func zrangebyscoreCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrangebyscore") + } + return zrangebyscoreGeneric(db, false, args...) +} + +func zrevrangebyscoreCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 3 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zrevrangebyscore") + } + return zrangebyscoreGeneric(db, true, args...) +} + +func zclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zclear") + } + + key := []byte(args[0]) + if n, err := db.ZClear(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zmclearCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) < 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zmclear") + } + + keys := make([][]byte, len(args)) + for i, arg := range args { + keys[i] = []byte(arg) + } + if n, err := db.ZMclear(keys...); err != nil { + return nil, err + } else { + return n, nil + } +} + +func zexpireCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zexpire") + } + + duration, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + if v, err := db.ZExpire(key, duration); err != nil { + return nil, err + } else { + return v, nil + } +} + +func zexpireAtCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 2 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zexpireat") + } + + when, err := strconv.ParseInt(args[1], 10, 64) + if err != nil { + return nil, ErrValue + } + + key := []byte(args[0]) + if v, err := db.ZExpireAt(key, when); err != nil { + return nil, err + } else { + return v, nil + } +} + +func zttlCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zttl") + } + + key := []byte(args[0]) + if v, err := db.ZTTL(key); err != nil { + return nil, err + } else { + return v, nil + } +} + +func zpersistCommand(db *ledis.DB, args ...string) (interface{}, error) { + if len(args) != 1 { + return nil, fmt.Errorf(ERR_ARGUMENT_FORMAT, "zpersist") + } + + key := []byte(args[0]) + if n, err := db.ZPersist(key); err != nil { + return nil, err + } else { + return n, nil + } +} + +func init() { + register("zadd", zaddCommand) + register("zcard", zcardCommand) + register("zcount", zcountCommand) + register("zincrby", zincrbyCommand) + register("zrange", zrangeCommand) + register("zrangebyscore", zrangebyscoreCommand) + register("zrank", zrankCommand) + register("zrem", zremCommand) + register("zremrangebyrank", zremrangebyrankCommand) + register("zremrangebyscore", zremrangebyscoreCommand) + register("zrevrange", zrevrangeCommand) + register("zrevrank", zrevrankCommand) + register("zrevrangebyscore", zrevrangebyscoreCommand) + register("zscore", zscoreCommand) + + //ledisdb special command + + register("zclear", zclearCommand) + register("zmclear", zmclearCommand) + register("zexpire", zexpireCommand) + register("zexpireat", zexpireAtCommand) + register("zttl", zttlCommand) + register("zpersist", zpersistCommand) +} diff --git a/server/http/handler.go b/server/http/handler.go index d395534..0c9ae1b 100644 --- a/server/http/handler.go +++ b/server/http/handler.go @@ -42,8 +42,9 @@ func (h *CmdHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *CmdHandler) parseReqPath(path string) (db int, cmd string, args []string) { /* - the proper format of `path` is /cmd/arg1/arg2/../argN or /db/cmd/arg1/arg2/../argN - if `path` is the first kind, `db` will be 0 + this function extracts `db`, `cmd` and `args` from `path` + the proper format of `path` is /cmd/arg1/arg2/../argN or /db/cmd/arg1/arg2/../argN + if `path` is the first kind, `db` will be 0 */ substrings := strings.Split(strings.TrimLeft(path, "/"), "/") if len(substrings) == 1 {