diff --git a/database/command.go b/database/command.go new file mode 100644 index 0000000..ba2b772 --- /dev/null +++ b/database/command.go @@ -0,0 +1,171 @@ +package database + +import ( + "github.com/hdt3213/godis/interface/redis" + "github.com/hdt3213/godis/redis/protocol" + "strings" +) + +func init() { + +} + +var commandTable = make(map[string]*godisCommand) + +const ( + Write = "write" + Readonly = "readonly" + Denyoom = "denyoom" + Admin = "admin" + Pubsub = "pubsub" + Noscript = "noscript" + Random = "random" + SortForScript = "sortforscript" + Loading = "loading" + Stale = "stale" + SkipMonitor = "skip_monitor" + Asking = "asking" + Fast = "fast" + Movablekeys = "movablekeys" +) + +type godisCommand struct { + name string + arity int + signs []string + firstKey int + lastKey int + stepNumber int + prepare PreFunc +} + +func RegisterGodisCommand(name string, arity int, signs []string, firstKey int, lastKey int, stepNumber int, prepare PreFunc) { + name = strings.ToLower(name) + commandTable[name] = &godisCommand{ + name: name, + arity: arity, + signs: signs, + firstKey: firstKey, + lastKey: lastKey, + stepNumber: stepNumber, + prepare: prepare, + } +} + +func execCommand(args [][]byte) redis.Reply { + n := len(args) + if n > 1 { + subCommand := strings.ToUpper(string(args[1])) + if subCommand == "INFO" { + return getCommands(args[2:]) + } else if subCommand == "COUNT" { + return protocol.MakeIntReply(int64(len(commandTable))) + } else if subCommand == "GETKEYS" { + if n < 2 { + return protocol.MakeErrReply("Unknown subcommand or wrong number of arguments for '" + subCommand + "'") + } + return getKeys(args[2:]) + } else { + return protocol.MakeErrReply("Unknow subcomand or wrong number of arguments for '" + subCommand + "'") + } + } else { + return getAllGodisCommandReply() + } +} + +func getKeys(args [][]byte) redis.Reply { + key := string(args[0]) + command, ok := commandTable[key] + if !ok { + return protocol.MakeErrReply("Invalid command specified") + } + arity := command.arity + if arity > 0 { + if len(args) != arity { + return protocol.MakeErrReply("Invalid number of arguments specified for command") + } + } else { + if len(args) < -arity { + return protocol.MakeErrReply("Invalid number of arguments specified for command") + } + } + + prepare := command.prepare + if prepare == nil { + return protocol.MakeErrReply("The command has no key arguments") + } + writeKeys, readKeys := prepare(args[1:]) + + keys := append(writeKeys, readKeys...) + replies := make([]redis.Reply, len(keys)) + for i, key := range keys { + replies[i] = protocol.MakeBulkReply([]byte(key)) + } + return protocol.MakeMultiRawReply(replies) +} + +func getCommands(args [][]byte) redis.Reply { + replies := make([]redis.Reply, len(args)) + + for i, v := range args { + reply, ok := commandTable[string(v)] + if ok { + replies[i] = reply.ToReply() + } else { + replies[i] = protocol.MakeNullBulkReply() + } + } + + return protocol.MakeMultiRawReply(replies) +} + +func getAllGodisCommandReply() redis.Reply { + replies := make([]redis.Reply, len(commandTable)) + i := 0 + for _, v := range commandTable { + replies[i] = v.ToReply() + i++ + } + return protocol.MakeMultiRawReply(replies) +} + +func (g *godisCommand) ToReply() redis.Reply { + args := make([]redis.Reply, 6) + args[0] = protocol.MakeBulkReply([]byte(g.name)) + args[1] = protocol.MakeIntReply(int64(g.arity)) + signs := make([]redis.Reply, len(g.signs)) + for i, v := range g.signs { + signs[i] = protocol.MakeStatusReply(v) + } + args[2] = protocol.MakeMultiRawReply(signs) + args[3] = protocol.MakeIntReply(int64(g.firstKey)) + args[4] = protocol.MakeIntReply(int64(g.lastKey)) + args[5] = protocol.MakeIntReply(int64(g.stepNumber)) + + return protocol.MakeMultiRawReply(args) +} + +func init() { + RegisterGodisCommand("Command", 0, []string{Random, Loading, Stale}, 0, 0, 0, nil) + + RegisterGodisCommand("Keys", 2, []string{Readonly, SortForScript}, 0, 0, 0, nil) + RegisterGodisCommand("Auth", 2, []string{Noscript, Loading, Stale, SkipMonitor, Fast}, 0, 0, 0, nil) + RegisterGodisCommand("Info", -1, []string{Random, Loading, Stale}, 0, 0, 0, nil) + RegisterGodisCommand("Slaveof", 3, []string{Admin, Noscript, Stale}, 0, 0, 0, nil) + RegisterGodisCommand("Subscribe", -2, []string{Pubsub, Noscript, Loading, Stale}, 0, 0, 0, nil) + RegisterGodisCommand("Publish", 3, []string{Pubsub, Noscript, Loading, Fast}, 0, 0, 0, nil) + RegisterGodisCommand("FlushAll", -1, []string{Write}, 0, 0, 0, nil) + RegisterGodisCommand("FlushDb", -1, []string{Write}, 0, 0, 0, nil) + RegisterGodisCommand("Save", 1, []string{Admin, Noscript}, 0, 0, 0, nil) + RegisterGodisCommand("BgSave", 1, []string{Admin, Noscript}, 0, 0, 0, nil) + RegisterGodisCommand("Select", 2, []string{Loading, Fast}, 0, 0, 0, nil) + RegisterGodisCommand("Replconf", -1, []string{Admin, Noscript, Loading, Stale}, 0, 0, 0, nil) + RegisterGodisCommand("Replconf", 3, []string{Readonly, Admin, Noscript}, 0, 0, 0, nil) + + // transaction command + RegisterGodisCommand("Multi", 1, []string{Noscript, Fast}, 0, 0, 0, nil) + RegisterGodisCommand("Discard", 1, []string{Noscript, Fast}, 0, 0, 0, nil) + RegisterGodisCommand("Exec", 1, []string{Noscript, SkipMonitor}, 0, 0, 0, nil) + RegisterGodisCommand("Watch", 1, []string{Noscript, Fast}, 1, -1, 1, readAllKeys) + +} diff --git a/database/geo.go b/database/geo.go index f53bda2..5c00c18 100644 --- a/database/geo.go +++ b/database/geo.go @@ -269,3 +269,13 @@ func init() { RegisterCommand("GeoRadius", execGeoRadius, readFirstKey, nil, -6, flagReadOnly) RegisterCommand("GeoRadiusByMember", execGeoRadiusByMember, readFirstKey, nil, -5, flagReadOnly) } + +func init() { + RegisterGodisCommand("GeoAdd", -5, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("GeoPos", -2, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("GeoDist", -4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("GeoHash", -2, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("GeoRadius", -6, []string{Write, Movablekeys}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("GeoRadiusByMember", -5, []string{Write, Movablekeys}, 1, 1, 1, readFirstKey) + +} diff --git a/database/hash.go b/database/hash.go index aa8215f..d518061 100644 --- a/database/hash.go +++ b/database/hash.go @@ -515,3 +515,22 @@ func init() { RegisterCommand("HIncrByFloat", execHIncrByFloat, writeFirstKey, undoHIncr, 4, flagWrite) RegisterCommand("HRandField", execHRandField, readFirstKey, nil, -2, flagReadOnly) } + +func init() { + RegisterGodisCommand("HSet", 4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HSetNX", 4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HGet", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HExists", 3, []string{Readonly, Fast}, 1, 1, 1, readAllKeys) + RegisterGodisCommand("HDel", -3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HLen", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HStrlen", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HMSet", -1, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HMGet", -3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HGet", -3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HKeys", 2, []string{Readonly, SortForScript}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HVals", 2, []string{Readonly, SortForScript}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HGetAll", 2, []string{Readonly, Random}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("HIncrBy", 4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HIncrByFloat", 4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("HRandField", -2, []string{Random, Readonly}, 1, 1, 1, readFirstKey) +} diff --git a/database/keys.go b/database/keys.go index 7328dc5..1f028ef 100644 --- a/database/keys.go +++ b/database/keys.go @@ -426,3 +426,21 @@ func init() { RegisterCommand("RenameNx", execRenameNx, prepareRename, undoRename, 3, flagReadOnly) RegisterCommand("Keys", execKeys, noPrepare, nil, 2, flagReadOnly) } + +func init() { + RegisterGodisCommand("Del", -2, []string{Write}, 1, -1, 1, writeAllKeys) + RegisterGodisCommand("Expire", 3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ExpireAt", 3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ExpireTime", 2, []string{Write, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("PExpire", 3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("PExpireAt", 3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("PExpireTime", 2, []string{Write, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("TTL", 2, []string{Readonly, Random, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("PTTL", 2, []string{Readonly, Random, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("Persist", 2, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("Exists", -2, []string{Readonly, Fast}, 1, 1, 1, readAllKeys) + RegisterGodisCommand("Type", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("Rename", 3, []string{Write}, 1, 1, 1, prepareRename) + RegisterGodisCommand("RenameNx", 3, []string{Write, Fast}, 1, 1, 1, prepareRename) + RegisterGodisCommand("Keys", 2, []string{Readonly, SortForScript}, 1, 1, 1, nil) +} diff --git a/database/list.go b/database/list.go index 17a9c96..8d0ada8 100644 --- a/database/list.go +++ b/database/list.go @@ -524,3 +524,18 @@ func init() { RegisterCommand("LSet", execLSet, writeFirstKey, undoLSet, 4, flagWrite) RegisterCommand("LRange", execLRange, readFirstKey, nil, 4, flagReadOnly) } + +func init() { + RegisterGodisCommand("LPush", -3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("LPushX", -3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("RPush", -3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("RPushX", -3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("LPop", 2, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("RPop", 2, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("RPopLPush", 3, []string{Write, Denyoom}, 1, 1, 1, prepareRPopLPush) + RegisterGodisCommand("LRem", 4, []string{Write}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("LLen", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("LIndex", 3, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("LSet", 4, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("LRange", 4, []string{Readonly}, 1, 1, 1, readFirstKey) +} diff --git a/database/server.go b/database/server.go index da857f1..6289ea3 100644 --- a/database/server.go +++ b/database/server.go @@ -107,6 +107,8 @@ func (server *Server) Exec(c redis.Connection, cmdLine [][]byte) (result redis.R return protocol.MakeArgNumErrReply("SLAVEOF") } return server.execSlaveOf(c, cmdLine[1:]) + } else if cmdName == "command" { + return execCommand(cmdLine) } // read only slave diff --git a/database/set.go b/database/set.go index e2ee089..48845b5 100644 --- a/database/set.go +++ b/database/set.go @@ -493,3 +493,19 @@ func init() { RegisterCommand("SDiffStore", execSDiffStore, prepareSetCalculateStore, rollbackFirstKey, -3, flagWrite) RegisterCommand("SRandMember", execSRandMember, readFirstKey, nil, -2, flagReadOnly) } + +func init() { + RegisterGodisCommand("SAdd", -3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SIsMember", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("SRem", -3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SPop", -2, []string{Write, Random, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SCard", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("SMembers", 2, []string{Readonly, SortForScript}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("SInter", -2, []string{Readonly, SortForScript}, 1, -1, 1, prepareSetCalculate) + RegisterGodisCommand("SInterStore", -3, []string{Write, Denyoom}, 1, -1, 1, prepareSetCalculateStore) + RegisterGodisCommand("SUnion", -2, []string{Readonly, SortForScript}, 1, -1, 1, prepareSetCalculate) + RegisterGodisCommand("SUnionStore", -3, []string{Write, Denyoom}, 1, -1, 1, prepareSetCalculateStore) + RegisterGodisCommand("SDiff", -2, []string{Readonly, SortForScript}, 1, 1, 1, prepareSetCalculate) + RegisterGodisCommand("SDiffStore", -3, []string{Write, Denyoom}, 1, 1, 1, prepareSetCalculateStore) + RegisterGodisCommand("SRandMember", -2, []string{Readonly, Random}, 1, 1, 1, readFirstKey) +} diff --git a/database/sortedset.go b/database/sortedset.go index c1012ae..7e51644 100644 --- a/database/sortedset.go +++ b/database/sortedset.go @@ -638,3 +638,21 @@ func init() { RegisterCommand("ZRemRangeByScore", execZRemRangeByScore, writeFirstKey, rollbackFirstKey, 4, flagWrite) RegisterCommand("ZRemRangeByRank", execZRemRangeByRank, writeFirstKey, rollbackFirstKey, 4, flagWrite) } + +func init() { + RegisterGodisCommand("ZAdd", -4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ZScore", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZIncrBy", 4, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ZRank", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZCount", 4, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZRevRank", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZCard", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZRange", -4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZRangeByScore", -4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZRevRange", -4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZRevRangeByScore", -4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("ZPopMin", -2, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ZRem", -3, []string{Write, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ZRemRangeByScore", 4, []string{Write}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("ZRemRangeByRank", 4, []string{Write}, 1, 1, 1, writeFirstKey) +} diff --git a/database/string.go b/database/string.go index d7318aa..e69a7de 100644 --- a/database/string.go +++ b/database/string.go @@ -865,3 +865,32 @@ func init() { RegisterCommand("Randomkey", getRandomKey, readAllKeys, nil, 1, flagReadOnly) } + +func init() { + RegisterGodisCommand("Set", -3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SetNx", 3, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SetEX", 4, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("PSetEX", 4, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("MSet", -3, []string{Write, Denyoom}, 1, -1, 2, prepareMSet) + RegisterGodisCommand("MGet", -2, []string{Readonly, Fast}, 1, 1, 1, prepareMSet) + RegisterGodisCommand("MSetNX", -3, []string{Write, Denyoom}, 1, 1, 1, prepareMSet) + RegisterGodisCommand("Get", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("GetEX", -2, []string{Readonly, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("GetSet", 3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("GetDel", 2, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("Incr", 2, []string{Write, Denyoom, Fast}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("IncrBy", 3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("IncrByFloat", 3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("Decr", 2, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("DecrBy", 3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("StrLen", 2, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("Append", 3, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("SetRange", 4, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("GetRange", 4, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("SetBit", 4, []string{Write, Denyoom}, 1, 1, 1, writeFirstKey) + RegisterGodisCommand("GetBit", 3, []string{Readonly, Fast}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("BitCount", -2, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("BitPos", -3, []string{Readonly}, 1, 1, 1, readFirstKey) + RegisterGodisCommand("Randomkey", 1, []string{Readonly, Random}, 1, 1, 1, readAllKeys) + +}