diff options
| -rw-r--r-- | handlers.go | 672 | ||||
| -rw-r--r-- | main.go | 33 | ||||
| -rw-r--r-- | pkg/commands/command-registry.go | 73 | ||||
| -rw-r--r-- | pkg/commands/dbsize.go | 15 | ||||
| -rw-r--r-- | pkg/commands/expire.go | 26 | ||||
| -rw-r--r-- | pkg/commands/flushall.go | 14 | ||||
| -rw-r--r-- | pkg/commands/hget.go | 25 | ||||
| -rw-r--r-- | pkg/commands/hgetall.go | 20 | ||||
| -rw-r--r-- | pkg/commands/hkeys.go | 24 | ||||
| -rw-r--r-- | pkg/commands/hset.go | 32 | ||||
| -rw-r--r-- | pkg/commands/hvalues.go | 24 | ||||
| -rw-r--r-- | pkg/commands/keys.go | 41 | ||||
| -rw-r--r-- | pkg/commands/lrem.go | 75 | ||||
| -rw-r--r-- | pkg/commands/ping.go | 16 | ||||
| -rw-r--r-- | pkg/commands/type.go | 24 | ||||
| -rwxr-xr-x | redisClone | bin | 0 -> 3632615 bytes |
16 files changed, 409 insertions, 705 deletions
diff --git a/handlers.go b/handlers.go deleted file mode 100644 index 1569771..0000000 --- a/handlers.go +++ /dev/null @@ -1,672 +0,0 @@ -package main - -import ( - "errors" - "net" - "path/filepath" - "redisClone/pkg/core" - "sort" - "strconv" - "time" -) - -func handleGet(args []string)interface{} { - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'GET'") - return err - } - - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{}{ - item, exists := s.Data[key] - if !exists { - return nil - } - - if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { - delete(s.Data, key) - return nil - } - - strValue, ok := item.Value.(string) - if !ok { - err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - return err - } - return strValue - }) - - return result -} - -func handleExists(args []string)interface{} { - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'EXISTS'") - return err - } - - key := args[1] - result := db.ExecuteRead(key, func(s *core.Shard) interface{}{ - _, exists := s.Data[key] - if exists { - return 1 - } else { - return 0 - } - }) - - return result -} - -func handleSet(args []string)interface{} { - if len(args) != 3 { - err := errors.New("wrong number of arguments for 'SET'") - return err - } - - key := args[1] - result := db.Execute(key, func(s *core.Shard) interface{}{ - item := core.Item{Value: args[2]} - s.Data[key] = item - return core.SimpleString("OK") - }) - - return result -} - -func handleDel(args []string)interface{} { - - if len(args) < 2 { - err := errors.New("wrong number of arguments for 'DEL'") - return err - } - keys := args[1:] - deletedKeys := db.ExecuteMulti(keys, func(shards []*core.Shard) interface{}{ - count := 0 - for _, key := range keys{ - shard := db.GetShard(key) - _, exists := shard.Data[key] - if exists { - delete(shard.Data, key) - count += 1 - } - } - return count - }) - - return deletedKeys -} - - -func handleIncr(args []string)interface{} { - - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'INCR'") - return err - } - - key := args[1] - result := db.Execute(key, func(s *core.Shard)interface{}{ - item, exists := s.Data[key] - if exists { - strValue, ok := item.Value.(string) - if !ok { - err := errors.New("value is not an integer or out of range") - return err - } - - currentInt, err := strconv.Atoi(strValue) - if err != nil { - err := errors.New("value is not an integer or out of range") - return err - } - - newValueStr := strconv.Itoa(currentInt + 1) - s.Data[key] = core.Item{Value: newValueStr} - return currentInt + 1 - } else { - s.Data[key] = core.Item{Value: "1"} - return 1 - } - - }) - - return result -} - -func handleDecr(args []string)interface{} { - - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'DECR'") - return err - } - - key := args[1] - result := db.Execute(key, func(s *core.Shard)interface{}{ - item, exists := s.Data[key] - if exists { - strValue, ok := item.Value.(string) - if !ok { - err := errors.New("value is not an integer or out of range") - return err - } - - currentInt, err := strconv.Atoi(strValue) - if err != nil { - err := errors.New("value is not an integer or out of range") - return err - } - - newValueStr := strconv.Itoa(currentInt + 1) - s.Data[key] = core.Item{Value: newValueStr} - return currentInt - 1 - } else { - s.Data[key] = core.Item{Value: "1"} - return -1 - } - - }) - return result -} - -func handlePing(conn net.Conn, args []string) { - conn.Write(core.SerializeRESP(core.SimpleString("OK"))) -} - -func handleFlushall(args []string)interface{} { - if len(args) != 1 { - err := errors.New("wrong number of arguments for 'FLUSHALL'") - return err - } - result := db.ExecuteAll(func(shards []*core.Shard)interface{}{ - for _, shard := range shards{ - shard.Data = make(map[string]core.Item) - } - return core.SimpleString("OK") - }) - return result -} - -func handleRename(args []string)interface{}{ - if len(args) != 3 { - err := errors.New("wrong number of arguments for 'RENAME'") - return err - } - keys := args[1:] - - result := db.ExecuteMulti(keys,func(shards []*core.Shard)interface{}{ - item, exists := db.GetShard(keys[0]).Data[keys[0]] - if !exists { - - return errors.New("no such key") - } - - db.GetShard(keys[1]).Data[keys[1]] = item - delete(db.GetShard(keys[0]).Data, keys[0]) - - return core.SimpleString("OK") - }) - return result -} - -func handleRpush(args []string)interface{} { - if len(args) < 3 { - err := errors.New("wrong number of arguments for 'RPUSH'") - return err - } - key := args[1] - newItems := args[2:] - - result := db.Execute(key, func(s *core.Shard) interface{} { - var list []string - existingItem, exists := s.Data[key] - if exists { - var ok bool - list, ok = existingItem.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - } else { - list = []string{} - } - list = append(list, newItems...) - s.Data[key] = core.Item{Value: list} - return len(list) - }) - return result -} - -func handleLpush(args []string)interface{} { - if len(args) < 3 { - err := errors.New("wrong number of arguments for 'LPUSH'") - return err - } - key := args[1] - newItems := args[2:] - - result := db.Execute(key, func(s *core.Shard) interface{} { - var list []string - existingItem, exists := s.Data[key] - if exists { - var ok bool - list, ok = existingItem.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - } else { - list = []string{} - } - list = append(newItems, list...) - s.Data[key] = core.Item{Value: list} - return len(list) - }) - - return result -} - -func handleLrange(args []string)interface{} { - if len(args) != 4 { - err := errors.New("wrong number of arguments for 'LRANGE'") - return err - } - start, err1 := strconv.Atoi(args[2]) - stop, err2 := strconv.Atoi(args[3]) - if err1 != nil || err2 != nil { - err := errors.New("value is not an integer or out of range") - return err - } - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return []string{} - } - if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { - delete(s.Data, key) - return nil - } - typedList, ok := item.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - size := len(typedList) - if start < 0 { - start += size - if start < 0 { - start = 0 - } - } - if stop < 0 { - stop += size - if stop < 0 { - stop = 0 - } - } - if stop >= size { - stop = size - 1 - } - if start > stop || start >= size { - return []string{} - } - return typedList[start : stop+1] - }) - return result -} - -func handleLpop(args []string)interface{} { - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'LPOP'") - return err - } - key := args[1] - - result := db.Execute(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - typedList, ok := item.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - if len(typedList) == 0 { - return nil - } - newItem := typedList[0] - newList := typedList[1:] - if len(newList) <= 0 { - delete(s.Data, key) - } else { - s.Data[key] = core.Item{Value: newList} - } - return newItem - }) - return result -} - -func handleRpop(args []string)interface{} { - if len(args) != 2 { - err := errors.New("wrong number of arguments for 'RPOP'") - return err - } - key := args[1] - - result := db.Execute(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - typedList, ok := item.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - if len(typedList) == 0 { - return nil - } - newItem := typedList[len(typedList)-1] - newList := typedList[:len(typedList)-1] - if len(newList) <= 0 { - delete(s.Data, key) - } else { - s.Data[key] = core.Item{Value: newList} - } - return newItem - }) - return result -} - -func handleExpire(args []string)interface{} { - if len(args) != 3 { - return errors.New("wrong number of arguments for 'EXPIRE'") - } - key := args[1] - seconds, err := strconv.Atoi(args[2]) - if err != nil { - return errors.New("value is not an integer") - } - - result := db.Execute(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return 0 - } - expiry := time.Now().Add(time.Duration(seconds) * time.Second) - item.ExpiresAt = &expiry - s.Data[key] = item - return 1 - }) - return result -} - -func handleHset(args []string)interface{}{ - if len(args) < 4 || (len(args)-2)%2 != 0 { - err := errors.New("wrong number of arguments for 'HSET'") - return err - } - key := args[1] - - result := db.Execute(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - var hash map[string]string - if exists { - var ok bool - hash, ok = item.Value.(map[string]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - } else { - hash = make(map[string]string) - } - count := 0 - for i := 2; i < len(args); i += 2 { - hash[args[i]] = args[i+1] - count++ - } - s.Data[key] = core.Item{Value: hash} - return count - }) - return result -} - -func handleHget(args []string)interface{} { - if len(args) != 3 { - return errors.New("wrong number of arguments for 'HGET'") - } - key := args[1] - field := args[2] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - hash, ok := item.Value.(map[string]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - val, found := hash[field] - if !found { - return nil - } - return val - }) - return result -} - -func handleHgetall(args []string)interface{} { - if len(args) != 2 { - return errors.New("wrong number of arguments for 'HGETALL'") - } - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - hash, ok := item.Value.(map[string]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - return hash - }) - return result -} - -func handleHkeys(args []string)interface{} { - if len(args) != 2 { - return errors.New("wrong number of arguments for 'HKEYS'") - } - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - hash, ok := item.Value.(map[string]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - keys := make([]string, 0, len(hash)) - for k := range hash { - keys = append(keys, k) - } - return keys - }) - return result -} - -func handleHvalues(args []string)interface{} { - if len(args) != 2 { - return errors.New("wrong number of arguments for 'HVALUES'") - } - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return nil - } - hash, ok := item.Value.(map[string]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - values := make([]string, 0, len(hash)) - for _, v := range hash { - values = append(values, v) - } - return values - }) - return result -} - -func handleType(args []string)interface{} { - if len(args) != 2 { - return errors.New("wrong number of arguments for 'TYPE'") - } - key := args[1] - - result := db.ExecuteRead(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return core.SimpleString("none") - } - switch item.Value.(type) { - case string: - return core.SimpleString("string") - case []string: - return core.SimpleString("list") - case map[string]string: - return core.SimpleString("hash") - default: - return core.SimpleString("unknown") - } - }) - return result -} - -func handleDbsize(args []string)interface{} { - if len(args) != 1 { - return errors.New("wrong number of arguments for 'DBSIZE'") - } - result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} { - count := 0 - for _, shard := range shards { - count += len(shard.Data) - } - return count - }) - return result -} - -func handleKeys(args []string)interface{} { - if len(args) < 2 || len(args) > 3 { - return errors.New("wrong number of arguments for 'KEYS'") - } - pattern := args[1] - limit := -1 - if len(args) == 3 { - val, err := strconv.Atoi(args[2]) - if err != nil { - return errors.New("value is not an integer") - } - limit = val - } - - result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} { - var matches []string - for _, shard := range shards { - for key := range shard.Data { - matched, err := filepath.Match(pattern, key) - if err != nil { - return errors.New("illegal glob pattern") - } - if matched { - matches = append(matches, key) - } - } - } - sort.Strings(matches) - if limit != -1 && len(matches) > limit { - matches = matches[:limit] - } - return matches - }) - return result -} - -func handleLrem(args []string)interface{} { - if len(args) != 4 { - return errors.New("wrong number of arguments for 'LREM'") - } - key := args[1] - count, err := strconv.Atoi(args[2]) - toBeRemoved := args[3] - if err != nil { - return errors.New("value is not an integer") - } - - result := db.Execute(key, func(s *core.Shard) interface{} { - item, exists := s.Data[key] - if !exists { - return 0 - } - list, ok := item.Value.([]string) - if !ok { - return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") - } - - counter := 0 - absCount := count - if count < 0 { - absCount = -count - } - - var newList []string - if count == 0 { - newList = []string{} - for _, value := range list { - if value == toBeRemoved { - counter++ - } else { - newList = append(newList, value) - } - } - } else if count > 0 { - newList = []string{} - for _, value := range list { - if value == toBeRemoved && counter < absCount { - counter++ - } else { - newList = append(newList, value) - } - } - } else { // count < 0 - reversedNewList := []string{} - for i := len(list) - 1; i >= 0; i-- { - value := list[i] - if value == toBeRemoved && counter < absCount { - counter++ - } else { - reversedNewList = append(reversedNewList, value) - } - } - newList = make([]string, len(reversedNewList)) - for i, v := range reversedNewList { - newList[len(reversedNewList)-1-i] = v - } - } - - if len(newList) == 0 { - delete(s.Data, key) - } else { - item.Value = newList - s.Data[key] = item - } - return counter - }) - return result -}
\ No newline at end of file @@ -17,38 +17,6 @@ import ( var db core.RedisDB var hub pubsub.Hub -type baseHandlerFunc func([]string)interface{} -type connectionHandlerFunc func(net.Conn, []string) - -var baseCommandRegistry = map[string]baseHandlerFunc{ - "GET": handleGet, - "SET": handleSet, - "DEL": handleDel, - "EXISTS": handleExists, - "INCR": handleIncr, - "DECR": handleDecr, - "RENAME": handleRename, - "FLUSHALL": handleFlushall, - "RPUSH": handleRpush, - "LPUSH": handleLpush, - "LRANGE": handleLrange, - "LPOP": handleLpop, - "RPOP": handleRpop, - "EXPIRE": handleExpire, - "HSET": handleHset, - "HGET": handleHget, - "HGETALL": handleHgetall, - "HKEYS": handleHkeys, - "HVALUES": handleHvalues, - "TYPE": handleType, - "DBSIZE": handleDbsize, - "KEYS": handleKeys, - "LREM": handleLrem, -} -var connectionCommandRegistry = map[string]connectionHandlerFunc{ - "PING": handlePing, -} - func main() { shards := make([]*core.Shard, core.NumShards) @@ -59,6 +27,7 @@ func main() { } } db = core.RedisDB{Shards: shards} + commands.SetDB(db) hub = pubsub.Hub{Channels: make(map[string]map[net.Conn]struct{}),} go func() { diff --git a/pkg/commands/command-registry.go b/pkg/commands/command-registry.go index baf7133..068fe7c 100644 --- a/pkg/commands/command-registry.go +++ b/pkg/commands/command-registry.go @@ -4,6 +4,13 @@ import ( "errors" "redisClone/pkg/core" ) + +var dbInstance core.RedisDB + +func SetDB(db core.RedisDB) { + dbInstance = db +} + func SingleKey(args []string) []string { return []string{args[1]} } @@ -16,6 +23,10 @@ func NoKeys(args []string) []string { return nil } +func RenameKeys(args []string) []string { + return []string{args[1], args[2]} +} + type CommandDef struct{ MinArgs int ExtractKeys func(args []string)[]string @@ -72,7 +83,7 @@ var Registry = map[string]CommandDef{ }, "RENAME": { MinArgs: 3, - ExtractKeys: SingleKey, + ExtractKeys: RenameKeys, Execute: Rename, }, "RPUSH": { @@ -100,4 +111,64 @@ var Registry = map[string]CommandDef{ ExtractKeys: SingleKey, Execute: Lrange, }, + "FLUSHALL": { + MinArgs: 1, + ExtractKeys: NoKeys, + Execute: Flushall, + }, + "EXPIRE": { + MinArgs: 3, + ExtractKeys: SingleKey, + Execute: Expire, + }, + "HSET": { + MinArgs: 4, + ExtractKeys: SingleKey, + Execute: Hset, + }, + "HGET": { + MinArgs: 3, + ExtractKeys: SingleKey, + Execute: Hget, + }, + "HGETALL": { + MinArgs: 2, + ExtractKeys: SingleKey, + Execute: Hgetall, + }, + "HKEYS": { + MinArgs: 2, + ExtractKeys: SingleKey, + Execute: Hkeys, + }, + "HVALUES": { + MinArgs: 2, + ExtractKeys: SingleKey, + Execute: Hvalues, + }, + "TYPE": { + MinArgs: 2, + ExtractKeys: SingleKey, + Execute: Type, + }, + "DBSIZE": { + MinArgs: 1, + ExtractKeys: NoKeys, + Execute: Dbsize, + }, + "KEYS": { + MinArgs: 2, + ExtractKeys: NoKeys, + Execute: Keys, + }, + "LREM": { + MinArgs: 4, + ExtractKeys: SingleKey, + Execute: Lrem, + }, + "PING": { + MinArgs: 1, + ExtractKeys: NoKeys, + Execute: Ping, + }, }
\ No newline at end of file diff --git a/pkg/commands/dbsize.go b/pkg/commands/dbsize.go new file mode 100644 index 0000000..dbe39a4 --- /dev/null +++ b/pkg/commands/dbsize.go @@ -0,0 +1,15 @@ +package commands + +import ( + "redisClone/pkg/core" +) + +func Dbsize(args []string, getShard func(k string) *core.Shard) interface{} { + return dbInstance.ExecuteReadAll(func(shards []*core.Shard) interface{} { + count := 0 + for _, shard := range shards { + count += len(shard.Data) + } + return count + }) +} diff --git a/pkg/commands/expire.go b/pkg/commands/expire.go new file mode 100644 index 0000000..4337e83 --- /dev/null +++ b/pkg/commands/expire.go @@ -0,0 +1,26 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" + "strconv" + "time" +) + +func Expire(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + seconds, err := strconv.Atoi(args[2]) + if err != nil { + return errors.New("value is not an integer") + } + + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return 0 + } + expiry := time.Now().Add(time.Duration(seconds) * time.Second) + item.ExpiresAt = &expiry + s.Data[key] = item + return 1 +} diff --git a/pkg/commands/flushall.go b/pkg/commands/flushall.go new file mode 100644 index 0000000..9f1316a --- /dev/null +++ b/pkg/commands/flushall.go @@ -0,0 +1,14 @@ +package commands + +import ( + "redisClone/pkg/core" +) + +func Flushall(args []string, getShard func(k string) *core.Shard) interface{} { + return dbInstance.ExecuteAll(func(shards []*core.Shard) interface{} { + for _, shard := range shards { + shard.Data = make(map[string]core.Item) + } + return core.SimpleString("OK") + }) +} diff --git a/pkg/commands/hget.go b/pkg/commands/hget.go new file mode 100644 index 0000000..7d16808 --- /dev/null +++ b/pkg/commands/hget.go @@ -0,0 +1,25 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" +) + +func Hget(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + field := args[2] + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + val, found := hash[field] + if !found { + return nil + } + return val +} diff --git a/pkg/commands/hgetall.go b/pkg/commands/hgetall.go new file mode 100644 index 0000000..02b3e3f --- /dev/null +++ b/pkg/commands/hgetall.go @@ -0,0 +1,20 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" +) + +func Hgetall(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + return hash +} diff --git a/pkg/commands/hkeys.go b/pkg/commands/hkeys.go new file mode 100644 index 0000000..09a5d97 --- /dev/null +++ b/pkg/commands/hkeys.go @@ -0,0 +1,24 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" +) + +func Hkeys(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + keys := make([]string, 0, len(hash)) + for k := range hash { + keys = append(keys, k) + } + return keys +} diff --git a/pkg/commands/hset.go b/pkg/commands/hset.go new file mode 100644 index 0000000..33f992f --- /dev/null +++ b/pkg/commands/hset.go @@ -0,0 +1,32 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" +) + +func Hset(args []string, getShard func(k string) *core.Shard) interface{} { + if (len(args)-2)%2 != 0 { + return errors.New("wrong number of arguments for 'HSET'") + } + key := args[1] + s := getShard(key) + item, exists := s.Data[key] + var hash map[string]string + if exists { + var ok bool + hash, ok = item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + } else { + hash = make(map[string]string) + } + count := 0 + for i := 2; i < len(args); i += 2 { + hash[args[i]] = args[i+1] + count++ + } + s.Data[key] = core.Item{Value: hash} + return count +} diff --git a/pkg/commands/hvalues.go b/pkg/commands/hvalues.go new file mode 100644 index 0000000..1034fbd --- /dev/null +++ b/pkg/commands/hvalues.go @@ -0,0 +1,24 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" +) + +func Hvalues(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return nil + } + hash, ok := item.Value.(map[string]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + values := make([]string, 0, len(hash)) + for _, v := range hash { + values = append(values, v) + } + return values +} diff --git a/pkg/commands/keys.go b/pkg/commands/keys.go new file mode 100644 index 0000000..b233964 --- /dev/null +++ b/pkg/commands/keys.go @@ -0,0 +1,41 @@ +package commands + +import ( + "errors" + "path/filepath" + "redisClone/pkg/core" + "sort" + "strconv" +) + +func Keys(args []string, getShard func(k string) *core.Shard) interface{} { + pattern := args[1] + limit := -1 + if len(args) == 3 { + val, err := strconv.Atoi(args[2]) + if err != nil { + return errors.New("value is not an integer") + } + limit = val + } + + return dbInstance.ExecuteReadAll(func(shards []*core.Shard) interface{} { + var matches []string + for _, shard := range shards { + for key := range shard.Data { + matched, err := filepath.Match(pattern, key) + if err != nil { + return errors.New("illegal glob pattern") + } + if matched { + matches = append(matches, key) + } + } + } + sort.Strings(matches) + if limit != -1 && len(matches) > limit { + matches = matches[:limit] + } + return matches + }) +} diff --git a/pkg/commands/lrem.go b/pkg/commands/lrem.go new file mode 100644 index 0000000..38be270 --- /dev/null +++ b/pkg/commands/lrem.go @@ -0,0 +1,75 @@ +package commands + +import ( + "errors" + "redisClone/pkg/core" + "strconv" +) + +func Lrem(args []string, getShard func(k string) *core.Shard) interface{} { + key := args[1] + count, err := strconv.Atoi(args[2]) + toBeRemoved := args[3] + if err != nil { + return errors.New("value is not an integer") + } + + s := getShard(key) + item, exists := s.Data[key] + if !exists { + return 0 + } + list, ok := item.Value.([]string) + if !ok { + return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") + } + + counter := 0 + absCount := count + if count < 0 { + absCount = -count + } + + var newList []string + if count == 0 { + newList = []string{} + for _, value := range list { + if value == toBeRemoved { + counter++ + } else { + newList = append(newList, value) + } + } + } else if count > 0 { + newList = []string{} + for _, value := range list { + if value == toBeRemoved && counter < absCount { + counter++ |
