1 #include "redis.h" 2 #include <math.h> 3 4 /*----------------------------------------------------------------------------- 5 * Hash type API 6 *----------------------------------------------------------------------------*/ 7 8 /* Check the length of a number of objects to see if we need to convert a 9 * ziplist to a real hash. Note that we only check string encoded objects 10 * as their string length can be queried in constant time. */ 11 void hashTypeTryConversion(robj *o, robj **argv, int start, int end) { 12 int i; 13 14 if (o->encoding != REDIS_ENCODING_ZIPLIST) return; 15 16 for (i = start; i <= end; i++) { 17 if (argv[i]->encoding == REDIS_ENCODING_RAW && 18 sdslen(argv[i]->ptr) > server.hash_max_ziplist_value) 19 { 20 hashTypeConvert(o, REDIS_ENCODING_HT); 21 break; 22 } 23 } 24 } 25 26 /* Encode given objects in-place when the hash uses a dict. */ 27 void hashTypeTryObjectEncoding(robj *subject, robj **o1, robj **o2) { 28 if (subject->encoding == REDIS_ENCODING_HT) { 29 if (o1) *o1 = tryObjectEncoding(*o1); 30 if (o2) *o2 = tryObjectEncoding(*o2); 31 } 32 } 33 34 /* Get the value from a ziplist encoded hash, identified by field. 35 * Returns -1 when the field cannot be found. */ 36 int hashTypeGetFromZiplist(robj *o, robj *field, 37 unsigned char **vstr, 38 unsigned int *vlen, 39 long long *vll) 40 { 41 unsigned char *zl, *fptr = NULL, *vptr = NULL; 42 int ret; 43 44 redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST); 45 46 field = getDecodedObject(field); 47 48 zl = o->ptr; 49 fptr = ziplistIndex(zl, ZIPLIST_HEAD); 50 if (fptr != NULL) { 51 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1); 52 if (fptr != NULL) { 53 /* Grab pointer to the value (fptr points to the field) */ 54 vptr = ziplistNext(zl, fptr); 55 redisAssert(vptr != NULL); 56 } 57 } 58 59 decrRefCount(field); 60 61 if (vptr != NULL) { 62 ret = ziplistGet(vptr, vstr, vlen, vll); 63 redisAssert(ret); 64 return 0; 65 } 66 67 return -1; 68 } 69 70 /* Get the value from a hash table encoded hash, identified by field. 71 * Returns -1 when the field cannot be found. */ 72 int hashTypeGetFromHashTable(robj *o, robj *field, robj **value) { 73 dictEntry *de; 74 75 redisAssert(o->encoding == REDIS_ENCODING_HT); 76 77 de = dictFind(o->ptr, field); 78 if (de == NULL) return -1; 79 *value = dictGetVal(de); 80 return 0; 81 } 82 83 /* Higher level function of hashTypeGet*() that always returns a Redis 84 * object (either new or with refcount incremented), so that the caller 85 * can retain a reference or call decrRefCount after the usage. 86 * 87 * The lower level function can prevent copy on write so it is 88 * the preferred way of doing read operations. */ 89 robj *hashTypeGetObject(robj *o, robj *field) { 90 robj *value = NULL; 91 92 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 93 unsigned char *vstr = NULL; 94 unsigned int vlen = UINT_MAX; 95 long long vll = LLONG_MAX; 96 97 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) { 98 if (vstr) { 99 value = createStringObject((char*)vstr, vlen); 100 } else { 101 value = createStringObjectFromLongLong(vll); 102 } 103 } 104 105 } else if (o->encoding == REDIS_ENCODING_HT) { 106 robj *aux; 107 108 if (hashTypeGetFromHashTable(o, field, &aux) == 0) { 109 incrRefCount(aux); 110 value = aux; 111 } 112 } else { 113 redisPanic("Unknown hash encoding"); 114 } 115 return value; 116 } 117 118 /* Test if the specified field exists in the given hash. Returns 1 if the field 119 * exists, and 0 when it doesn't. */ 120 int hashTypeExists(robj *o, robj *field) { 121 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 122 unsigned char *vstr = NULL; 123 unsigned int vlen = UINT_MAX; 124 long long vll = LLONG_MAX; 125 126 if (hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll) == 0) return 1; 127 } else if (o->encoding == REDIS_ENCODING_HT) { 128 robj *aux; 129 130 if (hashTypeGetFromHashTable(o, field, &aux) == 0) return 1; 131 } else { 132 redisPanic("Unknown hash encoding"); 133 } 134 return 0; 135 } 136 137 /* Add an element, discard the old if the key already exists. 138 * Return 0 on insert and 1 on update. 139 * This function will take care of incrementing the reference count of the 140 * retained fields and value objects. */ 141 int hashTypeSet(robj *o, robj *field, robj *value) { 142 int update = 0; 143 144 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 145 unsigned char *zl, *fptr, *vptr; 146 147 field = getDecodedObject(field); 148 value = getDecodedObject(value); 149 150 zl = o->ptr; 151 fptr = ziplistIndex(zl, ZIPLIST_HEAD); 152 if (fptr != NULL) { 153 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1); 154 if (fptr != NULL) { 155 /* Grab pointer to the value (fptr points to the field) */ 156 vptr = ziplistNext(zl, fptr); 157 redisAssert(vptr != NULL); 158 update = 1; 159 160 /* Delete value */ 161 zl = ziplistDelete(zl, &vptr); 162 163 /* Insert new value */ 164 zl = ziplistInsert(zl, vptr, value->ptr, sdslen(value->ptr)); 165 } 166 } 167 168 if (!update) { 169 /* Push new field/value pair onto the tail of the ziplist */ 170 zl = ziplistPush(zl, field->ptr, sdslen(field->ptr), ZIPLIST_TAIL); 171 zl = ziplistPush(zl, value->ptr, sdslen(value->ptr), ZIPLIST_TAIL); 172 } 173 o->ptr = zl; 174 decrRefCount(field); 175 decrRefCount(value); 176 177 /* Check if the ziplist needs to be converted to a hash table */ 178 if (hashTypeLength(o) > server.hash_max_ziplist_entries) 179 hashTypeConvert(o, REDIS_ENCODING_HT); 180 } else if (o->encoding == REDIS_ENCODING_HT) { 181 if (dictReplace(o->ptr, field, value)) { /* Insert */ 182 incrRefCount(field); 183 } else { /* Update */ 184 update = 1; 185 } 186 incrRefCount(value); 187 } else { 188 redisPanic("Unknown hash encoding"); 189 } 190 return update; 191 } 192 193 /* Delete an element from a hash. 194 * Return 1 on deleted and 0 on not found. */ 195 int hashTypeDelete(robj *o, robj *field) { 196 int deleted = 0; 197 198 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 199 unsigned char *zl, *fptr; 200 201 field = getDecodedObject(field); 202 203 zl = o->ptr; 204 fptr = ziplistIndex(zl, ZIPLIST_HEAD); 205 if (fptr != NULL) { 206 fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1); 207 if (fptr != NULL) { 208 zl = ziplistDelete(zl,&fptr); 209 zl = ziplistDelete(zl,&fptr); 210 o->ptr = zl; 211 deleted = 1; 212 } 213 } 214 215 decrRefCount(field); 216 217 } else if (o->encoding == REDIS_ENCODING_HT) { 218 if (dictDelete((dict*)o->ptr, field) == REDIS_OK) { 219 deleted = 1; 220 221 /* Always check if the dictionary needs a resize after a delete. */ 222 if (htNeedsResize(o->ptr)) dictResize(o->ptr); 223 } 224 225 } else { 226 redisPanic("Unknown hash encoding"); 227 } 228 229 return deleted; 230 } 231 232 /* Return the number of elements in a hash. */ 233 unsigned long hashTypeLength(robj *o) { 234 unsigned long length = ULONG_MAX; 235 236 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 237 length = ziplistLen(o->ptr) / 2; 238 } else if (o->encoding == REDIS_ENCODING_HT) { 239 length = dictSize((dict*)o->ptr); 240 } else { 241 redisPanic("Unknown hash encoding"); 242 } 243 244 return length; 245 } 246 247 hashTypeIterator *hashTypeInitIterator(robj *subject) { 248 hashTypeIterator *hi = zmalloc(sizeof(hashTypeIterator)); 249 hi->subject = subject; 250 hi->encoding = subject->encoding; 251 252 if (hi->encoding == REDIS_ENCODING_ZIPLIST) { 253 hi->fptr = NULL; 254 hi->vptr = NULL; 255 } else if (hi->encoding == REDIS_ENCODING_HT) { 256 hi->di = dictGetIterator(subject->ptr); 257 } else { 258 redisPanic("Unknown hash encoding"); 259 } 260 261 return hi; 262 } 263 264 void hashTypeReleaseIterator(hashTypeIterator *hi) { 265 if (hi->encoding == REDIS_ENCODING_HT) { 266 dictReleaseIterator(hi->di); 267 } 268 269 zfree(hi); 270 } 271 272 /* Move to the next entry in the hash. Return REDIS_OK when the next entry 273 * could be found and REDIS_ERR when the iterator reaches the end. */ 274 int hashTypeNext(hashTypeIterator *hi) { 275 if (hi->encoding == REDIS_ENCODING_ZIPLIST) { 276 unsigned char *zl; 277 unsigned char *fptr, *vptr; 278 279 zl = hi->subject->ptr; 280 fptr = hi->fptr; 281 vptr = hi->vptr; 282 283 if (fptr == NULL) { 284 /* Initialize cursor */ 285 redisAssert(vptr == NULL); 286 fptr = ziplistIndex(zl, 0); 287 } else { 288 /* Advance cursor */ 289 redisAssert(vptr != NULL); 290 fptr = ziplistNext(zl, vptr); 291 } 292 if (fptr == NULL) return REDIS_ERR; 293 294 /* Grab pointer to the value (fptr points to the field) */ 295 vptr = ziplistNext(zl, fptr); 296 redisAssert(vptr != NULL); 297 298 /* fptr, vptr now point to the first or next pair */ 299 hi->fptr = fptr; 300 hi->vptr = vptr; 301 } else if (hi->encoding == REDIS_ENCODING_HT) { 302 if ((hi->de = dictNext(hi->di)) == NULL) return REDIS_ERR; 303 } else { 304 redisPanic("Unknown hash encoding"); 305 } 306 return REDIS_OK; 307 } 308 309 /* Get the field or value at iterator cursor, for an iterator on a hash value 310 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */ 311 void hashTypeCurrentFromZiplist(hashTypeIterator *hi, int what, 312 unsigned char **vstr, 313 unsigned int *vlen, 314 long long *vll) 315 { 316 int ret; 317 318 redisAssert(hi->encoding == REDIS_ENCODING_ZIPLIST); 319 320 if (what & REDIS_HASH_KEY) { 321 ret = ziplistGet(hi->fptr, vstr, vlen, vll); 322 redisAssert(ret); 323 } else { 324 ret = ziplistGet(hi->vptr, vstr, vlen, vll); 325 redisAssert(ret); 326 } 327 } 328 329 /* Get the field or value at iterator cursor, for an iterator on a hash value 330 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */ 331 void hashTypeCurrentFromHashTable(hashTypeIterator *hi, int what, robj **dst) { 332 redisAssert(hi->encoding == REDIS_ENCODING_HT); 333 334 if (what & REDIS_HASH_KEY) { 335 *dst = dictGetKey(hi->de); 336 } else { 337 *dst = dictGetVal(hi->de); 338 } 339 } 340 341 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*() 342 * that returns an object with incremented refcount (or a new object). It is up 343 * to the caller to decrRefCount() the object if no reference is retained. */ 344 robj *hashTypeCurrentObject(hashTypeIterator *hi, int what) { 345 robj *dst; 346 347 if (hi->encoding == REDIS_ENCODING_ZIPLIST) { 348 unsigned char *vstr = NULL; 349 unsigned int vlen = UINT_MAX; 350 long long vll = LLONG_MAX; 351 352 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll); 353 if (vstr) { 354 dst = createStringObject((char*)vstr, vlen); 355 } else { 356 dst = createStringObjectFromLongLong(vll); 357 } 358 359 } else if (hi->encoding == REDIS_ENCODING_HT) { 360 hashTypeCurrentFromHashTable(hi, what, &dst); 361 incrRefCount(dst); 362 363 } else { 364 redisPanic("Unknown hash encoding"); 365 } 366 367 return dst; 368 } 369 370 robj *hashTypeLookupWriteOrCreate(redisClient *c, robj *key) { 371 robj *o = lookupKeyWrite(c->db,key); 372 if (o == NULL) { 373 o = createHashObject(); 374 dbAdd(c->db,key,o); 375 } else { 376 if (o->type != REDIS_HASH) { 377 addReply(c,shared.wrongtypeerr); 378 return NULL; 379 } 380 } 381 return o; 382 } 383 384 void hashTypeConvertZiplist(robj *o, int enc) { 385 redisAssert(o->encoding == REDIS_ENCODING_ZIPLIST); 386 387 if (enc == REDIS_ENCODING_ZIPLIST) { 388 /* Nothing to do... */ 389 390 } else if (enc == REDIS_ENCODING_HT) { 391 hashTypeIterator *hi; 392 dict *dict; 393 int ret; 394 395 hi = hashTypeInitIterator(o); 396 dict = dictCreate(&hashDictType, NULL); 397 398 while (hashTypeNext(hi) != REDIS_ERR) { 399 robj *field, *value; 400 401 field = hashTypeCurrentObject(hi, REDIS_HASH_KEY); 402 field = tryObjectEncoding(field); 403 value = hashTypeCurrentObject(hi, REDIS_HASH_VALUE); 404 value = tryObjectEncoding(value); 405 ret = dictAdd(dict, field, value); 406 redisAssert(ret == DICT_OK); 407 } 408 409 hashTypeReleaseIterator(hi); 410 zfree(o->ptr); 411 412 o->encoding = REDIS_ENCODING_HT; 413 o->ptr = dict; 414 415 } else { 416 redisPanic("Unknown hash encoding"); 417 } 418 } 419 420 void hashTypeConvert(robj *o, int enc) { 421 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 422 hashTypeConvertZiplist(o, enc); 423 } else if (o->encoding == REDIS_ENCODING_HT) { 424 redisPanic("Not implemented"); 425 } else { 426 redisPanic("Unknown hash encoding"); 427 } 428 } 429 430 /*----------------------------------------------------------------------------- 431 * Hash type commands 432 *----------------------------------------------------------------------------*/ 433 434 void hsetCommand(redisClient *c) { 435 int update; 436 robj *o; 437 438 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; 439 hashTypeTryConversion(o,c->argv,2,3); 440 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]); 441 update = hashTypeSet(o,c->argv[2],c->argv[3]); 442 addReply(c, update ? shared.czero : shared.cone); 443 signalModifiedKey(c->db,c->argv[1]); 444 server.dirty++; 445 } 446 447 void hsetnxCommand(redisClient *c) { 448 robj *o; 449 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; 450 hashTypeTryConversion(o,c->argv,2,3); 451 452 if (hashTypeExists(o, c->argv[2])) { 453 addReply(c, shared.czero); 454 } else { 455 hashTypeTryObjectEncoding(o,&c->argv[2], &c->argv[3]); 456 hashTypeSet(o,c->argv[2],c->argv[3]); 457 addReply(c, shared.cone); 458 signalModifiedKey(c->db,c->argv[1]); 459 server.dirty++; 460 } 461 } 462 463 void hmsetCommand(redisClient *c) { 464 int i; 465 robj *o; 466 467 if ((c->argc % 2) == 1) { 468 addReplyError(c,"wrong number of arguments for HMSET"); 469 return; 470 } 471 472 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; 473 hashTypeTryConversion(o,c->argv,2,c->argc-1); 474 for (i = 2; i < c->argc; i += 2) { 475 hashTypeTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]); 476 hashTypeSet(o,c->argv[i],c->argv[i+1]); 477 } 478 addReply(c, shared.ok); 479 signalModifiedKey(c->db,c->argv[1]); 480 server.dirty++; 481 } 482 483 void hincrbyCommand(redisClient *c) { 484 long long value, incr, oldvalue; 485 robj *o, *current, *new; 486 487 if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return; 488 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; 489 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) { 490 if (getLongLongFromObjectOrReply(c,current,&value, 491 "hash value is not an integer") != REDIS_OK) { 492 decrRefCount(current); 493 return; 494 } 495 decrRefCount(current); 496 } else { 497 value = 0; 498 } 499 500 oldvalue = value; 501 if ((incr < 0 && oldvalue < 0 && incr < (LLONG_MIN-oldvalue)) || 502 (incr > 0 && oldvalue > 0 && incr > (LLONG_MAX-oldvalue))) { 503 addReplyError(c,"increment or decrement would overflow"); 504 return; 505 } 506 value += incr; 507 new = createStringObjectFromLongLong(value); 508 hashTypeTryObjectEncoding(o,&c->argv[2],NULL); 509 hashTypeSet(o,c->argv[2],new); 510 decrRefCount(new); 511 addReplyLongLong(c,value); 512 signalModifiedKey(c->db,c->argv[1]); 513 server.dirty++; 514 } 515 516 void hincrbyfloatCommand(redisClient *c) { 517 double long value, incr; 518 robj *o, *current, *new, *aux; 519 520 if (getLongDoubleFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return; 521 if ((o = hashTypeLookupWriteOrCreate(c,c->argv[1])) == NULL) return; 522 if ((current = hashTypeGetObject(o,c->argv[2])) != NULL) { 523 if (getLongDoubleFromObjectOrReply(c,current,&value, 524 "hash value is not a valid float") != REDIS_OK) { 525 decrRefCount(current); 526 return; 527 } 528 decrRefCount(current); 529 } else { 530 value = 0; 531 } 532 533 value += incr; 534 new = createStringObjectFromLongDouble(value); 535 hashTypeTryObjectEncoding(o,&c->argv[2],NULL); 536 hashTypeSet(o,c->argv[2],new); 537 addReplyBulk(c,new); 538 signalModifiedKey(c->db,c->argv[1]); 539 server.dirty++; 540 541 /* Always replicate HINCRBYFLOAT as an HSET command with the final value 542 * in order to make sure that differences in float pricision or formatting 543 * will not create differences in replicas or after an AOF restart. */ 544 aux = createStringObject("HSET",4); 545 rewriteClientCommandArgument(c,0,aux); 546 decrRefCount(aux); 547 rewriteClientCommandArgument(c,3,new); 548 decrRefCount(new); 549 } 550 551 static void addHashFieldToReply(redisClient *c, robj *o, robj *field) { 552 int ret; 553 554 if (o == NULL) { 555 addReply(c, shared.nullbulk); 556 return; 557 } 558 559 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 560 unsigned char *vstr = NULL; 561 unsigned int vlen = UINT_MAX; 562 long long vll = LLONG_MAX; 563 564 ret = hashTypeGetFromZiplist(o, field, &vstr, &vlen, &vll); 565 if (ret < 0) { 566 addReply(c, shared.nullbulk); 567 } else { 568 if (vstr) { 569 addReplyBulkCBuffer(c, vstr, vlen); 570 } else { 571 addReplyBulkLongLong(c, vll); 572 } 573 } 574 575 } else if (o->encoding == REDIS_ENCODING_HT) { 576 robj *value; 577 578 ret = hashTypeGetFromHashTable(o, field, &value); 579 if (ret < 0) { 580 addReply(c, shared.nullbulk); 581 } else { 582 addReplyBulk(c, value); 583 } 584 585 } else { 586 redisPanic("Unknown hash encoding"); 587 } 588 } 589 590 void hgetCommand(redisClient *c) { 591 robj *o; 592 593 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL || 594 checkType(c,o,REDIS_HASH)) return; 595 596 addHashFieldToReply(c, o, c->argv[2]); 597 } 598 599 void hmgetCommand(redisClient *c) { 600 robj *o; 601 int i; 602 603 /* Don't abort when the key cannot be found. Non-existing keys are empty 604 * hashes, where HMGET should respond with a series of null bulks. */ 605 o = lookupKeyRead(c->db, c->argv[1]); 606 if (o != NULL && o->type != REDIS_HASH) { 607 addReply(c, shared.wrongtypeerr); 608 return; 609 } 610 611 addReplyMultiBulkLen(c, c->argc-2); 612 for (i = 2; i < c->argc; i++) { 613 addHashFieldToReply(c, o, c->argv[i]); 614 } 615 } 616 617 void hdelCommand(redisClient *c) { 618 robj *o; 619 int j, deleted = 0; 620 621 if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL || 622 checkType(c,o,REDIS_HASH)) return; 623 624 for (j = 2; j < c->argc; j++) { 625 if (hashTypeDelete(o,c->argv[j])) { 626 deleted++; 627 if (hashTypeLength(o) == 0) { 628 dbDelete(c->db,c->argv[1]); 629 break; 630 } 631 } 632 } 633 if (deleted) { 634 signalModifiedKey(c->db,c->argv[1]); 635 server.dirty += deleted; 636 } 637 addReplyLongLong(c,deleted); 638 } 639 640 void hlenCommand(redisClient *c) { 641 robj *o; 642 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 643 checkType(c,o,REDIS_HASH)) return; 644 645 addReplyLongLong(c,hashTypeLength(o)); 646 } 647 648 static void addHashIteratorCursorToReply(redisClient *c, hashTypeIterator *hi, int what) { 649 if (hi->encoding == REDIS_ENCODING_ZIPLIST) { 650 unsigned char *vstr = NULL; 651 unsigned int vlen = UINT_MAX; 652 long long vll = LLONG_MAX; 653 654 hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll); 655 if (vstr) { 656 addReplyBulkCBuffer(c, vstr, vlen); 657 } else { 658 addReplyBulkLongLong(c, vll); 659 } 660 661 } else if (hi->encoding == REDIS_ENCODING_HT) { 662 robj *value; 663 664 hashTypeCurrentFromHashTable(hi, what, &value); 665 addReplyBulk(c, value); 666 667 } else { 668 redisPanic("Unknown hash encoding"); 669 } 670 } 671 672 void genericHgetallCommand(redisClient *c, int flags) { 673 robj *o; 674 hashTypeIterator *hi; 675 int multiplier = 0; 676 int length, count = 0; 677 678 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL 679 || checkType(c,o,REDIS_HASH)) return; 680 681 if (flags & REDIS_HASH_KEY) multiplier++; 682 if (flags & REDIS_HASH_VALUE) multiplier++; 683 684 length = hashTypeLength(o) * multiplier; 685 addReplyMultiBulkLen(c, length); 686 687 hi = hashTypeInitIterator(o); 688 while (hashTypeNext(hi) != REDIS_ERR) { 689 if (flags & REDIS_HASH_KEY) { 690 addHashIteratorCursorToReply(c, hi, REDIS_HASH_KEY); 691 count++; 692 } 693 if (flags & REDIS_HASH_VALUE) { 694 addHashIteratorCursorToReply(c, hi, REDIS_HASH_VALUE); 695 count++; 696 } 697 } 698 699 hashTypeReleaseIterator(hi); 700 redisAssert(count == length); 701 } 702 703 void hkeysCommand(redisClient *c) { 704 genericHgetallCommand(c,REDIS_HASH_KEY); 705 } 706 707 void hvalsCommand(redisClient *c) { 708 genericHgetallCommand(c,REDIS_HASH_VALUE); 709 } 710 711 void hgetallCommand(redisClient *c) { 712 genericHgetallCommand(c,REDIS_HASH_KEY|REDIS_HASH_VALUE); 713 } 714 715 void hexistsCommand(redisClient *c) { 716 robj *o; 717 if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL || 718 checkType(c,o,REDIS_HASH)) return; 719 720 addReply(c, hashTypeExists(o,c->argv[2]) ? shared.cone : shared.czero); 721 } 722