github.com/hdt3213/godis@v1.2.9/database/keys.go (about) 1 package database 2 3 import ( 4 "github.com/hdt3213/godis/aof" 5 "github.com/hdt3213/godis/datastruct/dict" 6 "github.com/hdt3213/godis/datastruct/list" 7 "github.com/hdt3213/godis/datastruct/set" 8 "github.com/hdt3213/godis/datastruct/sortedset" 9 "github.com/hdt3213/godis/interface/redis" 10 "github.com/hdt3213/godis/lib/utils" 11 "github.com/hdt3213/godis/lib/wildcard" 12 "github.com/hdt3213/godis/redis/protocol" 13 "strconv" 14 "strings" 15 "time" 16 ) 17 18 // execDel removes a key from db 19 func execDel(db *DB, args [][]byte) redis.Reply { 20 keys := make([]string, len(args)) 21 for i, v := range args { 22 keys[i] = string(v) 23 } 24 25 deleted := db.Removes(keys...) 26 if deleted > 0 { 27 db.addAof(utils.ToCmdLine3("del", args...)) 28 } 29 return protocol.MakeIntReply(int64(deleted)) 30 } 31 32 func undoDel(db *DB, args [][]byte) []CmdLine { 33 keys := make([]string, len(args)) 34 for i, v := range args { 35 keys[i] = string(v) 36 } 37 return rollbackGivenKeys(db, keys...) 38 } 39 40 // execExists checks if given key is existed in db 41 func execExists(db *DB, args [][]byte) redis.Reply { 42 result := int64(0) 43 for _, arg := range args { 44 key := string(arg) 45 _, exists := db.GetEntity(key) 46 if exists { 47 result++ 48 } 49 } 50 return protocol.MakeIntReply(result) 51 } 52 53 // execFlushDB removes all data in current db 54 // deprecated, use Server.flushDB 55 func execFlushDB(db *DB, args [][]byte) redis.Reply { 56 db.Flush() 57 db.addAof(utils.ToCmdLine3("flushdb", args...)) 58 return &protocol.OkReply{} 59 } 60 61 // execType returns the type of entity, including: string, list, hash, set and zset 62 func execType(db *DB, args [][]byte) redis.Reply { 63 key := string(args[0]) 64 entity, exists := db.GetEntity(key) 65 if !exists { 66 return protocol.MakeStatusReply("none") 67 } 68 switch entity.Data.(type) { 69 case []byte: 70 return protocol.MakeStatusReply("string") 71 case list.List: 72 return protocol.MakeStatusReply("list") 73 case dict.Dict: 74 return protocol.MakeStatusReply("hash") 75 case *set.Set: 76 return protocol.MakeStatusReply("set") 77 case *sortedset.SortedSet: 78 return protocol.MakeStatusReply("zset") 79 } 80 return &protocol.UnknownErrReply{} 81 } 82 83 func prepareRename(args [][]byte) ([]string, []string) { 84 src := string(args[0]) 85 dest := string(args[1]) 86 return []string{dest}, []string{src} 87 } 88 89 // execRename a key 90 func execRename(db *DB, args [][]byte) redis.Reply { 91 if len(args) != 2 { 92 return protocol.MakeErrReply("ERR wrong number of arguments for 'rename' command") 93 } 94 src := string(args[0]) 95 dest := string(args[1]) 96 97 entity, ok := db.GetEntity(src) 98 if !ok { 99 return protocol.MakeErrReply("no such key") 100 } 101 rawTTL, hasTTL := db.ttlMap.Get(src) 102 db.PutEntity(dest, entity) 103 db.Remove(src) 104 if hasTTL { 105 db.Persist(src) // clean src and dest with their ttl 106 db.Persist(dest) 107 expireTime, _ := rawTTL.(time.Time) 108 db.Expire(dest, expireTime) 109 } 110 db.addAof(utils.ToCmdLine3("rename", args...)) 111 return &protocol.OkReply{} 112 } 113 114 func undoRename(db *DB, args [][]byte) []CmdLine { 115 src := string(args[0]) 116 dest := string(args[1]) 117 return rollbackGivenKeys(db, src, dest) 118 } 119 120 // execRenameNx a key, only if the new key does not exist 121 func execRenameNx(db *DB, args [][]byte) redis.Reply { 122 src := string(args[0]) 123 dest := string(args[1]) 124 125 _, ok := db.GetEntity(dest) 126 if ok { 127 return protocol.MakeIntReply(0) 128 } 129 130 entity, ok := db.GetEntity(src) 131 if !ok { 132 return protocol.MakeErrReply("no such key") 133 } 134 rawTTL, hasTTL := db.ttlMap.Get(src) 135 db.Removes(src, dest) // clean src and dest with their ttl 136 db.PutEntity(dest, entity) 137 if hasTTL { 138 db.Persist(src) // clean src and dest with their ttl 139 db.Persist(dest) 140 expireTime, _ := rawTTL.(time.Time) 141 db.Expire(dest, expireTime) 142 } 143 db.addAof(utils.ToCmdLine3("renamenx", args...)) 144 return protocol.MakeIntReply(1) 145 } 146 147 // execExpire sets a key's time to live in seconds 148 func execExpire(db *DB, args [][]byte) redis.Reply { 149 key := string(args[0]) 150 151 ttlArg, err := strconv.ParseInt(string(args[1]), 10, 64) 152 if err != nil { 153 return protocol.MakeErrReply("ERR value is not an integer or out of range") 154 } 155 ttl := time.Duration(ttlArg) * time.Second 156 157 _, exists := db.GetEntity(key) 158 if !exists { 159 return protocol.MakeIntReply(0) 160 } 161 162 expireAt := time.Now().Add(ttl) 163 db.Expire(key, expireAt) 164 db.addAof(aof.MakeExpireCmd(key, expireAt).Args) 165 return protocol.MakeIntReply(1) 166 } 167 168 // execExpireAt sets a key's expiration in unix timestamp 169 func execExpireAt(db *DB, args [][]byte) redis.Reply { 170 key := string(args[0]) 171 172 raw, err := strconv.ParseInt(string(args[1]), 10, 64) 173 if err != nil { 174 return protocol.MakeErrReply("ERR value is not an integer or out of range") 175 } 176 expireAt := time.Unix(raw, 0) 177 178 _, exists := db.GetEntity(key) 179 if !exists { 180 return protocol.MakeIntReply(0) 181 } 182 183 db.Expire(key, expireAt) 184 db.addAof(aof.MakeExpireCmd(key, expireAt).Args) 185 return protocol.MakeIntReply(1) 186 } 187 188 // execExpireTime returns the absolute Unix expiration timestamp in seconds at which the given key will expire. 189 func execExpireTime(db *DB, args [][]byte) redis.Reply { 190 key := string(args[0]) 191 _, exists := db.GetEntity(key) 192 if !exists { 193 return protocol.MakeIntReply(-2) 194 } 195 196 raw, exists := db.ttlMap.Get(key) 197 if !exists { 198 return protocol.MakeIntReply(-1) 199 } 200 rawExpireTime, _ := raw.(time.Time) 201 expireTime := rawExpireTime.Unix() 202 return protocol.MakeIntReply(expireTime) 203 } 204 205 // execPExpire sets a key's time to live in milliseconds 206 func execPExpire(db *DB, args [][]byte) redis.Reply { 207 key := string(args[0]) 208 209 ttlArg, err := strconv.ParseInt(string(args[1]), 10, 64) 210 if err != nil { 211 return protocol.MakeErrReply("ERR value is not an integer or out of range") 212 } 213 ttl := time.Duration(ttlArg) * time.Millisecond 214 215 _, exists := db.GetEntity(key) 216 if !exists { 217 return protocol.MakeIntReply(0) 218 } 219 220 expireAt := time.Now().Add(ttl) 221 db.Expire(key, expireAt) 222 db.addAof(aof.MakeExpireCmd(key, expireAt).Args) 223 return protocol.MakeIntReply(1) 224 } 225 226 // execPExpireAt sets a key's expiration in unix timestamp specified in milliseconds 227 func execPExpireAt(db *DB, args [][]byte) redis.Reply { 228 key := string(args[0]) 229 230 raw, err := strconv.ParseInt(string(args[1]), 10, 64) 231 if err != nil { 232 return protocol.MakeErrReply("ERR value is not an integer or out of range") 233 } 234 expireAt := time.Unix(0, raw*int64(time.Millisecond)) 235 236 _, exists := db.GetEntity(key) 237 if !exists { 238 return protocol.MakeIntReply(0) 239 } 240 241 db.Expire(key, expireAt) 242 243 db.addAof(aof.MakeExpireCmd(key, expireAt).Args) 244 return protocol.MakeIntReply(1) 245 } 246 247 // execPExpireTime returns the absolute Unix expiration timestamp in milliseconds at which the given key will expire. 248 func execPExpireTime(db *DB, args [][]byte) redis.Reply { 249 key := string(args[0]) 250 _, exists := db.GetEntity(key) 251 if !exists { 252 return protocol.MakeIntReply(-2) 253 } 254 255 raw, exists := db.ttlMap.Get(key) 256 if !exists { 257 return protocol.MakeIntReply(-1) 258 } 259 rawExpireTime, _ := raw.(time.Time) 260 expireTime := rawExpireTime.UnixMilli() 261 return protocol.MakeIntReply(expireTime) 262 } 263 264 // execTTL returns a key's time to live in seconds 265 func execTTL(db *DB, args [][]byte) redis.Reply { 266 key := string(args[0]) 267 _, exists := db.GetEntity(key) 268 if !exists { 269 return protocol.MakeIntReply(-2) 270 } 271 272 raw, exists := db.ttlMap.Get(key) 273 if !exists { 274 return protocol.MakeIntReply(-1) 275 } 276 expireTime, _ := raw.(time.Time) 277 ttl := expireTime.Sub(time.Now()) 278 return protocol.MakeIntReply(int64(ttl / time.Second)) 279 } 280 281 // execPTTL returns a key's time to live in milliseconds 282 func execPTTL(db *DB, args [][]byte) redis.Reply { 283 key := string(args[0]) 284 _, exists := db.GetEntity(key) 285 if !exists { 286 return protocol.MakeIntReply(-2) 287 } 288 289 raw, exists := db.ttlMap.Get(key) 290 if !exists { 291 return protocol.MakeIntReply(-1) 292 } 293 expireTime, _ := raw.(time.Time) 294 ttl := expireTime.Sub(time.Now()) 295 return protocol.MakeIntReply(int64(ttl / time.Millisecond)) 296 } 297 298 // execPersist removes expiration from a key 299 func execPersist(db *DB, args [][]byte) redis.Reply { 300 key := string(args[0]) 301 _, exists := db.GetEntity(key) 302 if !exists { 303 return protocol.MakeIntReply(0) 304 } 305 306 _, exists = db.ttlMap.Get(key) 307 if !exists { 308 return protocol.MakeIntReply(0) 309 } 310 311 db.Persist(key) 312 db.addAof(utils.ToCmdLine3("persist", args...)) 313 return protocol.MakeIntReply(1) 314 } 315 316 // execKeys returns all keys matching the given pattern 317 func execKeys(db *DB, args [][]byte) redis.Reply { 318 pattern, err := wildcard.CompilePattern(string(args[0])) 319 if err != nil { 320 return protocol.MakeErrReply("ERR illegal wildcard") 321 } 322 result := make([][]byte, 0) 323 db.data.ForEach(func(key string, val interface{}) bool { 324 if pattern.IsMatch(key) { 325 result = append(result, []byte(key)) 326 } 327 return true 328 }) 329 return protocol.MakeMultiBulkReply(result) 330 } 331 332 func toTTLCmd(db *DB, key string) *protocol.MultiBulkReply { 333 raw, exists := db.ttlMap.Get(key) 334 if !exists { 335 // has no TTL 336 return protocol.MakeMultiBulkReply(utils.ToCmdLine("PERSIST", key)) 337 } 338 expireTime, _ := raw.(time.Time) 339 timestamp := strconv.FormatInt(expireTime.UnixNano()/1000/1000, 10) 340 return protocol.MakeMultiBulkReply(utils.ToCmdLine("PEXPIREAT", key, timestamp)) 341 } 342 343 func undoExpire(db *DB, args [][]byte) []CmdLine { 344 key := string(args[0]) 345 return []CmdLine{ 346 toTTLCmd(db, key).Args, 347 } 348 } 349 350 // execCopy usage: COPY source destination [DB destination-db] [REPLACE] 351 // This command copies the value stored at the source key to the destination key. 352 func execCopy(mdb *Server, conn redis.Connection, args [][]byte) redis.Reply { 353 dbIndex := conn.GetDBIndex() 354 db := mdb.mustSelectDB(dbIndex) // Current DB 355 replaceFlag := false 356 srcKey := string(args[0]) 357 destKey := string(args[1]) 358 359 // Parse options 360 if len(args) > 2 { 361 for i := 2; i < len(args); i++ { 362 arg := strings.ToLower(string(args[i])) 363 if arg == "db" { 364 if i+1 >= len(args) { 365 return &protocol.SyntaxErrReply{} 366 } 367 idx, err := strconv.Atoi(string(args[i+1])) 368 if err != nil { 369 return &protocol.SyntaxErrReply{} 370 } 371 if idx >= len(mdb.dbSet) || idx < 0 { 372 return protocol.MakeErrReply("ERR DB index is out of range") 373 } 374 dbIndex = idx 375 i++ 376 } else if arg == "replace" { 377 replaceFlag = true 378 } else { 379 return &protocol.SyntaxErrReply{} 380 } 381 } 382 } 383 384 if srcKey == destKey && dbIndex == conn.GetDBIndex() { 385 return protocol.MakeErrReply("ERR source and destination objects are the same") 386 } 387 388 // source key does not exist 389 src, exists := db.GetEntity(srcKey) 390 if !exists { 391 return protocol.MakeIntReply(0) 392 } 393 394 destDB := mdb.mustSelectDB(dbIndex) 395 if _, exists = destDB.GetEntity(destKey); exists != false { 396 // If destKey exists and there is no "replace" option 397 if replaceFlag == false { 398 return protocol.MakeIntReply(0) 399 } 400 } 401 402 destDB.PutEntity(destKey, src) 403 raw, exists := db.ttlMap.Get(srcKey) 404 if exists { 405 expire := raw.(time.Time) 406 destDB.Expire(destKey, expire) 407 } 408 mdb.AddAof(conn.GetDBIndex(), utils.ToCmdLine3("copy", args...)) 409 return protocol.MakeIntReply(1) 410 } 411 412 func init() { 413 registerCommand("Del", execDel, writeAllKeys, undoDel, -2, flagWrite). 414 attachCommandExtra([]string{redisFlagWrite}, 1, -1, 1) 415 registerCommand("Expire", execExpire, writeFirstKey, undoExpire, 3, flagWrite). 416 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 417 registerCommand("ExpireAt", execExpireAt, writeFirstKey, undoExpire, 3, flagWrite). 418 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 419 registerCommand("ExpireTime", execExpireTime, readFirstKey, nil, 2, flagReadOnly). 420 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 421 registerCommand("PExpire", execPExpire, writeFirstKey, undoExpire, 3, flagWrite). 422 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 423 registerCommand("PExpireAt", execPExpireAt, writeFirstKey, undoExpire, 3, flagWrite). 424 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 425 registerCommand("PExpireTime", execPExpireTime, readFirstKey, nil, 2, flagReadOnly). 426 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 427 registerCommand("TTL", execTTL, readFirstKey, nil, 2, flagReadOnly). 428 attachCommandExtra([]string{redisFlagReadonly, redisFlagRandom, redisFlagFast}, 1, 1, 1) 429 registerCommand("PTTL", execPTTL, readFirstKey, nil, 2, flagReadOnly). 430 attachCommandExtra([]string{redisFlagReadonly, redisFlagRandom, redisFlagFast}, 1, 1, 1) 431 registerCommand("Persist", execPersist, writeFirstKey, undoExpire, 2, flagWrite). 432 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 433 registerCommand("Exists", execExists, readAllKeys, nil, -2, flagReadOnly). 434 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 435 registerCommand("Type", execType, readFirstKey, nil, 2, flagReadOnly). 436 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 437 registerCommand("Rename", execRename, prepareRename, undoRename, 3, flagReadOnly). 438 attachCommandExtra([]string{redisFlagWrite}, 1, 1, 1) 439 registerCommand("RenameNx", execRenameNx, prepareRename, undoRename, 3, flagReadOnly). 440 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 441 registerCommand("Keys", execKeys, noPrepare, nil, 2, flagReadOnly). 442 attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1) 443 }