github.com/hdt3213/godis@v1.2.9/database/hash.go (about) 1 package database 2 3 import ( 4 Dict "github.com/hdt3213/godis/datastruct/dict" 5 "github.com/hdt3213/godis/interface/database" 6 "github.com/hdt3213/godis/interface/redis" 7 "github.com/hdt3213/godis/lib/utils" 8 "github.com/hdt3213/godis/redis/protocol" 9 "github.com/shopspring/decimal" 10 "strconv" 11 "strings" 12 ) 13 14 func (db *DB) getAsDict(key string) (Dict.Dict, protocol.ErrorReply) { 15 entity, exists := db.GetEntity(key) 16 if !exists { 17 return nil, nil 18 } 19 dict, ok := entity.Data.(Dict.Dict) 20 if !ok { 21 return nil, &protocol.WrongTypeErrReply{} 22 } 23 return dict, nil 24 } 25 26 func (db *DB) getOrInitDict(key string) (dict Dict.Dict, inited bool, errReply protocol.ErrorReply) { 27 dict, errReply = db.getAsDict(key) 28 if errReply != nil { 29 return nil, false, errReply 30 } 31 inited = false 32 if dict == nil { 33 dict = Dict.MakeSimple() 34 db.PutEntity(key, &database.DataEntity{ 35 Data: dict, 36 }) 37 inited = true 38 } 39 return dict, inited, nil 40 } 41 42 // execHSet sets field in hash table 43 func execHSet(db *DB, args [][]byte) redis.Reply { 44 // parse args 45 key := string(args[0]) 46 field := string(args[1]) 47 value := args[2] 48 49 // get or init entity 50 dict, _, errReply := db.getOrInitDict(key) 51 if errReply != nil { 52 return errReply 53 } 54 55 result := dict.Put(field, value) 56 db.addAof(utils.ToCmdLine3("hset", args...)) 57 return protocol.MakeIntReply(int64(result)) 58 } 59 60 func undoHSet(db *DB, args [][]byte) []CmdLine { 61 key := string(args[0]) 62 field := string(args[1]) 63 return rollbackHashFields(db, key, field) 64 } 65 66 // execHSetNX sets field in hash table only if field not exists 67 func execHSetNX(db *DB, args [][]byte) redis.Reply { 68 // parse args 69 key := string(args[0]) 70 field := string(args[1]) 71 value := args[2] 72 73 dict, _, errReply := db.getOrInitDict(key) 74 if errReply != nil { 75 return errReply 76 } 77 78 result := dict.PutIfAbsent(field, value) 79 if result > 0 { 80 db.addAof(utils.ToCmdLine3("hsetnx", args...)) 81 82 } 83 return protocol.MakeIntReply(int64(result)) 84 } 85 86 // execHGet gets field value of hash table 87 func execHGet(db *DB, args [][]byte) redis.Reply { 88 // parse args 89 key := string(args[0]) 90 field := string(args[1]) 91 92 // get entity 93 dict, errReply := db.getAsDict(key) 94 if errReply != nil { 95 return errReply 96 } 97 if dict == nil { 98 return &protocol.NullBulkReply{} 99 } 100 101 raw, exists := dict.Get(field) 102 if !exists { 103 return &protocol.NullBulkReply{} 104 } 105 value, _ := raw.([]byte) 106 return protocol.MakeBulkReply(value) 107 } 108 109 // execHExists checks if a hash field exists 110 func execHExists(db *DB, args [][]byte) redis.Reply { 111 // parse args 112 key := string(args[0]) 113 field := string(args[1]) 114 115 // get entity 116 dict, errReply := db.getAsDict(key) 117 if errReply != nil { 118 return errReply 119 } 120 if dict == nil { 121 return protocol.MakeIntReply(0) 122 } 123 124 _, exists := dict.Get(field) 125 if exists { 126 return protocol.MakeIntReply(1) 127 } 128 return protocol.MakeIntReply(0) 129 } 130 131 // execHDel deletes a hash field 132 func execHDel(db *DB, args [][]byte) redis.Reply { 133 // parse args 134 key := string(args[0]) 135 fields := make([]string, len(args)-1) 136 fieldArgs := args[1:] 137 for i, v := range fieldArgs { 138 fields[i] = string(v) 139 } 140 141 // get entity 142 dict, errReply := db.getAsDict(key) 143 if errReply != nil { 144 return errReply 145 } 146 if dict == nil { 147 return protocol.MakeIntReply(0) 148 } 149 150 deleted := 0 151 for _, field := range fields { 152 result := dict.Remove(field) 153 deleted += result 154 } 155 if dict.Len() == 0 { 156 db.Remove(key) 157 } 158 if deleted > 0 { 159 db.addAof(utils.ToCmdLine3("hdel", args...)) 160 } 161 162 return protocol.MakeIntReply(int64(deleted)) 163 } 164 165 func undoHDel(db *DB, args [][]byte) []CmdLine { 166 key := string(args[0]) 167 fields := make([]string, len(args)-1) 168 fieldArgs := args[1:] 169 for i, v := range fieldArgs { 170 fields[i] = string(v) 171 } 172 return rollbackHashFields(db, key, fields...) 173 } 174 175 // execHLen gets number of fields in hash table 176 func execHLen(db *DB, args [][]byte) redis.Reply { 177 // parse args 178 key := string(args[0]) 179 180 dict, errReply := db.getAsDict(key) 181 if errReply != nil { 182 return errReply 183 } 184 if dict == nil { 185 return protocol.MakeIntReply(0) 186 } 187 return protocol.MakeIntReply(int64(dict.Len())) 188 } 189 190 // execHStrlen Returns the string length of the value associated with field in the hash stored at key. 191 // If the key or the field do not exist, 0 is returned. 192 func execHStrlen(db *DB, args [][]byte) redis.Reply { 193 key := string(args[0]) 194 field := string(args[1]) 195 196 dict, errReply := db.getAsDict(key) 197 if errReply != nil { 198 return errReply 199 } 200 if dict == nil { 201 return protocol.MakeIntReply(0) 202 } 203 204 raw, exists := dict.Get(field) 205 if exists { 206 value, _ := raw.([]byte) 207 return protocol.MakeIntReply(int64(len(value))) 208 } 209 return protocol.MakeIntReply(0) 210 } 211 212 // execHMSet sets multi fields in hash table 213 func execHMSet(db *DB, args [][]byte) redis.Reply { 214 // parse args 215 if len(args)%2 != 1 { 216 return protocol.MakeSyntaxErrReply() 217 } 218 key := string(args[0]) 219 size := (len(args) - 1) / 2 220 fields := make([]string, size) 221 values := make([][]byte, size) 222 for i := 0; i < size; i++ { 223 fields[i] = string(args[2*i+1]) 224 values[i] = args[2*i+2] 225 } 226 227 // get or init entity 228 dict, _, errReply := db.getOrInitDict(key) 229 if errReply != nil { 230 return errReply 231 } 232 233 // put data 234 for i, field := range fields { 235 value := values[i] 236 dict.Put(field, value) 237 } 238 db.addAof(utils.ToCmdLine3("hmset", args...)) 239 return &protocol.OkReply{} 240 } 241 242 func undoHMSet(db *DB, args [][]byte) []CmdLine { 243 key := string(args[0]) 244 size := (len(args) - 1) / 2 245 fields := make([]string, size) 246 for i := 0; i < size; i++ { 247 fields[i] = string(args[2*i+1]) 248 } 249 return rollbackHashFields(db, key, fields...) 250 } 251 252 // execHMGet gets multi fields in hash table 253 func execHMGet(db *DB, args [][]byte) redis.Reply { 254 key := string(args[0]) 255 size := len(args) - 1 256 fields := make([]string, size) 257 for i := 0; i < size; i++ { 258 fields[i] = string(args[i+1]) 259 } 260 261 // get entity 262 result := make([][]byte, size) 263 dict, errReply := db.getAsDict(key) 264 if errReply != nil { 265 return errReply 266 } 267 if dict == nil { 268 return protocol.MakeMultiBulkReply(result) 269 } 270 271 for i, field := range fields { 272 value, ok := dict.Get(field) 273 if !ok { 274 result[i] = nil 275 } else { 276 bytes, _ := value.([]byte) 277 result[i] = bytes 278 } 279 } 280 return protocol.MakeMultiBulkReply(result) 281 } 282 283 // execHKeys gets all field names in hash table 284 func execHKeys(db *DB, args [][]byte) redis.Reply { 285 key := string(args[0]) 286 287 dict, errReply := db.getAsDict(key) 288 if errReply != nil { 289 return errReply 290 } 291 if dict == nil { 292 return &protocol.EmptyMultiBulkReply{} 293 } 294 295 fields := make([][]byte, dict.Len()) 296 i := 0 297 dict.ForEach(func(key string, val interface{}) bool { 298 fields[i] = []byte(key) 299 i++ 300 return true 301 }) 302 return protocol.MakeMultiBulkReply(fields[:i]) 303 } 304 305 // execHVals gets all field value in hash table 306 func execHVals(db *DB, args [][]byte) redis.Reply { 307 key := string(args[0]) 308 309 // get entity 310 dict, errReply := db.getAsDict(key) 311 if errReply != nil { 312 return errReply 313 } 314 if dict == nil { 315 return &protocol.EmptyMultiBulkReply{} 316 } 317 318 values := make([][]byte, dict.Len()) 319 i := 0 320 dict.ForEach(func(key string, val interface{}) bool { 321 values[i], _ = val.([]byte) 322 i++ 323 return true 324 }) 325 return protocol.MakeMultiBulkReply(values[:i]) 326 } 327 328 // execHGetAll gets all key-value entries in hash table 329 func execHGetAll(db *DB, args [][]byte) redis.Reply { 330 key := string(args[0]) 331 332 // get entity 333 dict, errReply := db.getAsDict(key) 334 if errReply != nil { 335 return errReply 336 } 337 if dict == nil { 338 return &protocol.EmptyMultiBulkReply{} 339 } 340 341 size := dict.Len() 342 result := make([][]byte, size*2) 343 i := 0 344 dict.ForEach(func(key string, val interface{}) bool { 345 result[i] = []byte(key) 346 i++ 347 result[i], _ = val.([]byte) 348 i++ 349 return true 350 }) 351 return protocol.MakeMultiBulkReply(result[:i]) 352 } 353 354 // execHIncrBy increments the integer value of a hash field by the given number 355 func execHIncrBy(db *DB, args [][]byte) redis.Reply { 356 key := string(args[0]) 357 field := string(args[1]) 358 rawDelta := string(args[2]) 359 delta, err := strconv.ParseInt(rawDelta, 10, 64) 360 if err != nil { 361 return protocol.MakeErrReply("ERR value is not an integer or out of range") 362 } 363 364 dict, _, errReply := db.getOrInitDict(key) 365 if errReply != nil { 366 return errReply 367 } 368 369 value, exists := dict.Get(field) 370 if !exists { 371 dict.Put(field, args[2]) 372 db.addAof(utils.ToCmdLine3("hincrby", args...)) 373 return protocol.MakeBulkReply(args[2]) 374 } 375 val, err := strconv.ParseInt(string(value.([]byte)), 10, 64) 376 if err != nil { 377 return protocol.MakeErrReply("ERR hash value is not an integer") 378 } 379 val += delta 380 bytes := []byte(strconv.FormatInt(val, 10)) 381 dict.Put(field, bytes) 382 db.addAof(utils.ToCmdLine3("hincrby", args...)) 383 return protocol.MakeBulkReply(bytes) 384 } 385 386 func undoHIncr(db *DB, args [][]byte) []CmdLine { 387 key := string(args[0]) 388 field := string(args[1]) 389 return rollbackHashFields(db, key, field) 390 } 391 392 // execHIncrByFloat increments the float value of a hash field by the given number 393 func execHIncrByFloat(db *DB, args [][]byte) redis.Reply { 394 key := string(args[0]) 395 field := string(args[1]) 396 rawDelta := string(args[2]) 397 delta, err := decimal.NewFromString(rawDelta) 398 if err != nil { 399 return protocol.MakeErrReply("ERR value is not a valid float") 400 } 401 402 // get or init entity 403 dict, _, errReply := db.getOrInitDict(key) 404 if errReply != nil { 405 return errReply 406 } 407 408 value, exists := dict.Get(field) 409 if !exists { 410 dict.Put(field, args[2]) 411 return protocol.MakeBulkReply(args[2]) 412 } 413 val, err := decimal.NewFromString(string(value.([]byte))) 414 if err != nil { 415 return protocol.MakeErrReply("ERR hash value is not a float") 416 } 417 result := val.Add(delta) 418 resultBytes := []byte(result.String()) 419 dict.Put(field, resultBytes) 420 db.addAof(utils.ToCmdLine3("hincrbyfloat", args...)) 421 return protocol.MakeBulkReply(resultBytes) 422 } 423 424 // execHRandField return a random field(or field-value) from the hash value stored at key. 425 func execHRandField(db *DB, args [][]byte) redis.Reply { 426 key := string(args[0]) 427 count := 1 428 withvalues := 0 429 430 if len(args) > 3 { 431 return protocol.MakeErrReply("ERR wrong number of arguments for 'hrandfield' command") 432 } 433 434 if len(args) == 3 { 435 if strings.ToLower(string(args[2])) == "withvalues" { 436 withvalues = 1 437 } else { 438 return protocol.MakeSyntaxErrReply() 439 } 440 } 441 442 if len(args) >= 2 { 443 count64, err := strconv.ParseInt(string(args[1]), 10, 64) 444 if err != nil { 445 return protocol.MakeErrReply("ERR value is not an integer or out of range") 446 } 447 count = int(count64) 448 } 449 450 dict, errReply := db.getAsDict(key) 451 if errReply != nil { 452 return errReply 453 } 454 if dict == nil { 455 return &protocol.EmptyMultiBulkReply{} 456 } 457 458 if count > 0 { 459 fields := dict.RandomDistinctKeys(count) 460 Numfield := len(fields) 461 if withvalues == 0 { 462 result := make([][]byte, Numfield) 463 for i, v := range fields { 464 result[i] = []byte(v) 465 } 466 return protocol.MakeMultiBulkReply(result) 467 } else { 468 result := make([][]byte, 2*Numfield) 469 for i, v := range fields { 470 result[2*i] = []byte(v) 471 raw, _ := dict.Get(v) 472 result[2*i+1] = raw.([]byte) 473 } 474 return protocol.MakeMultiBulkReply(result) 475 } 476 } else if count < 0 { 477 fields := dict.RandomKeys(-count) 478 Numfield := len(fields) 479 if withvalues == 0 { 480 result := make([][]byte, Numfield) 481 for i, v := range fields { 482 result[i] = []byte(v) 483 } 484 return protocol.MakeMultiBulkReply(result) 485 } else { 486 result := make([][]byte, 2*Numfield) 487 for i, v := range fields { 488 result[2*i] = []byte(v) 489 raw, _ := dict.Get(v) 490 result[2*i+1] = raw.([]byte) 491 } 492 return protocol.MakeMultiBulkReply(result) 493 } 494 } 495 496 // 'count' is 0 will reach. 497 return &protocol.EmptyMultiBulkReply{} 498 } 499 500 func init() { 501 registerCommand("HSet", execHSet, writeFirstKey, undoHSet, 4, flagWrite). 502 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 503 registerCommand("HSetNX", execHSetNX, writeFirstKey, undoHSet, 4, flagWrite). 504 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 505 registerCommand("HGet", execHGet, readFirstKey, nil, 3, flagReadOnly). 506 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 507 registerCommand("HExists", execHExists, readFirstKey, nil, 3, flagReadOnly). 508 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 509 registerCommand("HDel", execHDel, writeFirstKey, undoHDel, -3, flagWrite). 510 attachCommandExtra([]string{redisFlagWrite, redisFlagFast}, 1, 1, 1) 511 registerCommand("HLen", execHLen, readFirstKey, nil, 2, flagReadOnly). 512 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 513 registerCommand("HStrlen", execHStrlen, readFirstKey, nil, 3, flagReadOnly). 514 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 515 registerCommand("HMSet", execHMSet, writeFirstKey, undoHMSet, -4, flagWrite). 516 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 517 registerCommand("HMGet", execHMGet, readFirstKey, nil, -3, flagReadOnly). 518 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 519 registerCommand("HGet", execHGet, readFirstKey, nil, -3, flagReadOnly). 520 attachCommandExtra([]string{redisFlagReadonly, redisFlagFast}, 1, 1, 1) 521 registerCommand("HKeys", execHKeys, readFirstKey, nil, 2, flagReadOnly). 522 attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1) 523 registerCommand("HVals", execHVals, readFirstKey, nil, 2, flagReadOnly). 524 attachCommandExtra([]string{redisFlagReadonly, redisFlagSortForScript}, 1, 1, 1) 525 registerCommand("HGetAll", execHGetAll, readFirstKey, nil, 2, flagReadOnly). 526 attachCommandExtra([]string{redisFlagReadonly, redisFlagRandom}, 1, 1, 1) 527 registerCommand("HIncrBy", execHIncrBy, writeFirstKey, undoHIncr, 4, flagWrite). 528 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 529 registerCommand("HIncrByFloat", execHIncrByFloat, writeFirstKey, undoHIncr, 4, flagWrite). 530 attachCommandExtra([]string{redisFlagWrite, redisFlagDenyOOM, redisFlagFast}, 1, 1, 1) 531 registerCommand("HRandField", execHRandField, readFirstKey, nil, -2, flagReadOnly). 532 attachCommandExtra([]string{redisFlagRandom, redisFlagReadonly}, 1, 1, 1) 533 }