]>
git.saurik.com Git - redis.git/blob - src/t_hash.c
   4 /*----------------------------------------------------------------------------- 
   6  *----------------------------------------------------------------------------*/ 
   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
) { 
  14     if (o
->encoding 
!= REDIS_ENCODING_ZIPLIST
) return; 
  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
) 
  20             hashTypeConvert(o
, REDIS_ENCODING_HT
); 
  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
); 
  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
, 
  41     unsigned char *zl
, *fptr 
= NULL
, *vptr 
= NULL
; 
  44     redisAssert(o
->encoding 
== REDIS_ENCODING_ZIPLIST
); 
  46     field 
= getDecodedObject(field
); 
  49     fptr 
= ziplistIndex(zl
, ZIPLIST_HEAD
); 
  51         fptr 
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1); 
  53             /* Grab pointer to the value (fptr points to the field) */ 
  54             vptr 
= ziplistNext(zl
, fptr
); 
  55             redisAssert(vptr 
!= NULL
); 
  62         ret 
= ziplistGet(vptr
, vstr
, vlen
, vll
); 
  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
) { 
  75     redisAssert(o
->encoding 
== REDIS_ENCODING_HT
); 
  77     de 
= dictFind(o
->ptr
, field
); 
  78     if (de 
== NULL
) return -1; 
  79     *value 
= dictGetVal(de
); 
  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. 
  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
) { 
  92     if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
  93         unsigned char *vstr 
= NULL
; 
  94         unsigned int vlen 
= UINT_MAX
; 
  95         long long vll 
= LLONG_MAX
; 
  97         if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) { 
  99                 value 
= createStringObject((char*)vstr
, vlen
); 
 101                 value 
= createStringObjectFromLongLong(vll
); 
 105     } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 108         if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) { 
 113         redisPanic("Unknown hash encoding"); 
 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
; 
 126         if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) return 1; 
 127     } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 130         if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) return 1; 
 132         redisPanic("Unknown hash encoding"); 
 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
) { 
 144     if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 145         unsigned char *zl
, *fptr
, *vptr
; 
 147         field 
= getDecodedObject(field
); 
 148         value 
= getDecodedObject(value
); 
 151         fptr 
= ziplistIndex(zl
, ZIPLIST_HEAD
); 
 153             fptr 
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1); 
 155                 /* Grab pointer to the value (fptr points to the field) */ 
 156                 vptr 
= ziplistNext(zl
, fptr
); 
 157                 redisAssert(vptr 
!= NULL
); 
 161                 zl 
= ziplistDelete(zl
, &vptr
); 
 163                 /* Insert new value */ 
 164                 zl 
= ziplistInsert(zl
, vptr
, value
->ptr
, sdslen(value
->ptr
)); 
 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
); 
 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 */ 
 183         } else { /* Update */ 
 188         redisPanic("Unknown hash encoding"); 
 193 /* Delete an element from a hash. 
 194  * Return 1 on deleted and 0 on not found. */ 
 195 int hashTypeDelete(robj 
*o
, robj 
*field
) { 
 198     if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 199         unsigned char *zl
, *fptr
; 
 201         field 
= getDecodedObject(field
); 
 204         fptr 
= ziplistIndex(zl
, ZIPLIST_HEAD
); 
 206             fptr 
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1); 
 208                 zl 
= ziplistDelete(zl
,&fptr
); 
 209                 zl 
= ziplistDelete(zl
,&fptr
); 
 217     } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 218         if (dictDelete((dict
*)o
->ptr
, field
) == REDIS_OK
) { 
 221             /* Always check if the dictionary needs a resize after a delete. */ 
 222             if (htNeedsResize(o
->ptr
)) dictResize(o
->ptr
); 
 226         redisPanic("Unknown hash encoding"); 
 232 /* Return the number of elements in a hash. */ 
 233 unsigned long hashTypeLength(robj 
*o
) { 
 234     unsigned long length 
= ULONG_MAX
; 
 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
); 
 241         redisPanic("Unknown hash encoding"); 
 247 hashTypeIterator 
*hashTypeInitIterator(robj 
*subject
) { 
 248     hashTypeIterator 
*hi 
= zmalloc(sizeof(hashTypeIterator
)); 
 249     hi
->subject 
= subject
; 
 250     hi
->encoding 
= subject
->encoding
; 
 252     if (hi
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 255     } else if (hi
->encoding 
== REDIS_ENCODING_HT
) { 
 256         hi
->di 
= dictGetIterator(subject
->ptr
); 
 258         redisPanic("Unknown hash encoding"); 
 264 void hashTypeReleaseIterator(hashTypeIterator 
*hi
) { 
 265     if (hi
->encoding 
== REDIS_ENCODING_HT
) { 
 266         dictReleaseIterator(hi
->di
); 
 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
) { 
 277         unsigned char *fptr
, *vptr
; 
 279         zl 
= hi
->subject
->ptr
; 
 284             /* Initialize cursor */ 
 285             redisAssert(vptr 
== NULL
); 
 286             fptr 
= ziplistIndex(zl
, 0); 
 289             redisAssert(vptr 
!= NULL
); 
 290             fptr 
= ziplistNext(zl
, vptr
); 
 292         if (fptr 
== NULL
) return REDIS_ERR
; 
 294         /* Grab pointer to the value (fptr points to the field) */ 
 295         vptr 
= ziplistNext(zl
, fptr
); 
 296         redisAssert(vptr 
!= NULL
); 
 298         /* fptr, vptr now point to the first or next pair */ 
 301     } else if (hi
->encoding 
== REDIS_ENCODING_HT
) { 
 302         if ((hi
->de 
= dictNext(hi
->di
)) == NULL
) return REDIS_ERR
; 
 304         redisPanic("Unknown hash encoding"); 
 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
, 
 318     redisAssert(hi
->encoding 
== REDIS_ENCODING_ZIPLIST
); 
 320     if (what 
& REDIS_HASH_KEY
) { 
 321         ret 
= ziplistGet(hi
->fptr
, vstr
, vlen
, vll
); 
 324         ret 
= ziplistGet(hi
->vptr
, vstr
, vlen
, vll
); 
 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
); 
 334     if (what 
& REDIS_HASH_KEY
) { 
 335         *dst 
= dictGetKey(hi
->de
); 
 337         *dst 
= dictGetVal(hi
->de
); 
 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
) { 
 347     if (hi
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 348         unsigned char *vstr 
= NULL
; 
 349         unsigned int vlen 
= UINT_MAX
; 
 350         long long vll 
= LLONG_MAX
; 
 352         hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
); 
 354             dst 
= createStringObject((char*)vstr
, vlen
); 
 356             dst 
= createStringObjectFromLongLong(vll
); 
 359     } else if (hi
->encoding 
== REDIS_ENCODING_HT
) { 
 360         hashTypeCurrentFromHashTable(hi
, what
, &dst
); 
 364         redisPanic("Unknown hash encoding"); 
 370 robj 
*hashTypeLookupWriteOrCreate(redisClient 
*c
, robj 
*key
) { 
 371     robj 
*o 
= lookupKeyWrite(c
->db
,key
); 
 373         o 
= createHashObject(); 
 376         if (o
->type 
!= REDIS_HASH
) { 
 377             addReply(c
,shared
.wrongtypeerr
); 
 384 void hashTypeConvertZiplist(robj 
*o
, int enc
) { 
 385     redisAssert(o
->encoding 
== REDIS_ENCODING_ZIPLIST
); 
 387     if (enc 
== REDIS_ENCODING_ZIPLIST
) { 
 388         /* Nothing to do... */ 
 390     } else if (enc 
== REDIS_ENCODING_HT
) { 
 391         hashTypeIterator 
*hi
; 
 395         hi 
= hashTypeInitIterator(o
); 
 396         dict 
= dictCreate(&hashDictType
, NULL
); 
 398         while (hashTypeNext(hi
) != REDIS_ERR
) { 
 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             if (ret 
!= DICT_OK
) { 
 407                 redisLogHexDump(REDIS_WARNING
,"ziplist with dup elements dump", 
 408                     o
->ptr
,ziplistBlobLen(o
->ptr
)); 
 409                 redisAssert(ret 
== DICT_OK
); 
 413         hashTypeReleaseIterator(hi
); 
 416         o
->encoding 
= REDIS_ENCODING_HT
; 
 420         redisPanic("Unknown hash encoding"); 
 424 void hashTypeConvert(robj 
*o
, int enc
) { 
 425     if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 426         hashTypeConvertZiplist(o
, enc
); 
 427     } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 428         redisPanic("Not implemented"); 
 430         redisPanic("Unknown hash encoding"); 
 434 /*----------------------------------------------------------------------------- 
 436  *----------------------------------------------------------------------------*/ 
 438 void hsetCommand(redisClient 
*c
) { 
 442     if ((o 
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return; 
 443     hashTypeTryConversion(o
,c
->argv
,2,3); 
 444     hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]); 
 445     update 
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]); 
 446     addReply(c
, update 
? shared
.czero 
: shared
.cone
); 
 447     signalModifiedKey(c
->db
,c
->argv
[1]); 
 451 void hsetnxCommand(redisClient 
*c
) { 
 453     if ((o 
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return; 
 454     hashTypeTryConversion(o
,c
->argv
,2,3); 
 456     if (hashTypeExists(o
, c
->argv
[2])) { 
 457         addReply(c
, shared
.czero
); 
 459         hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]); 
 460         hashTypeSet(o
,c
->argv
[2],c
->argv
[3]); 
 461         addReply(c
, shared
.cone
); 
 462         signalModifiedKey(c
->db
,c
->argv
[1]); 
 467 void hmsetCommand(redisClient 
*c
) { 
 471     if ((c
->argc 
% 2) == 1) { 
 472         addReplyError(c
,"wrong number of arguments for HMSET"); 
 476     if ((o 
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return; 
 477     hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1); 
 478     for (i 
= 2; i 
< c
->argc
; i 
+= 2) { 
 479         hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]); 
 480         hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]); 
 482     addReply(c
, shared
.ok
); 
 483     signalModifiedKey(c
->db
,c
->argv
[1]); 
 487 void hincrbyCommand(redisClient 
*c
) { 
 488     long long value
, incr
, oldvalue
; 
 489     robj 
*o
, *current
, *new; 
 491     if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return; 
 492     if ((o 
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return; 
 493     if ((current 
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) { 
 494         if (getLongLongFromObjectOrReply(c
,current
,&value
, 
 495             "hash value is not an integer") != REDIS_OK
) { 
 496             decrRefCount(current
); 
 499         decrRefCount(current
); 
 505     if ((incr 
< 0 && oldvalue 
< 0 && incr 
< (LLONG_MIN
-oldvalue
)) || 
 506         (incr 
> 0 && oldvalue 
> 0 && incr 
> (LLONG_MAX
-oldvalue
))) { 
 507         addReplyError(c
,"increment or decrement would overflow"); 
 511     new = createStringObjectFromLongLong(value
); 
 512     hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
); 
 513     hashTypeSet(o
,c
->argv
[2],new); 
 515     addReplyLongLong(c
,value
); 
 516     signalModifiedKey(c
->db
,c
->argv
[1]); 
 520 void hincrbyfloatCommand(redisClient 
*c
) { 
 521     double long value
, incr
; 
 522     robj 
*o
, *current
, *new, *aux
; 
 524     if (getLongDoubleFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return; 
 525     if ((o 
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return; 
 526     if ((current 
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) { 
 527         if (getLongDoubleFromObjectOrReply(c
,current
,&value
, 
 528             "hash value is not a valid float") != REDIS_OK
) { 
 529             decrRefCount(current
); 
 532         decrRefCount(current
); 
 538     new = createStringObjectFromLongDouble(value
); 
 539     hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
); 
 540     hashTypeSet(o
,c
->argv
[2],new); 
 542     signalModifiedKey(c
->db
,c
->argv
[1]); 
 545     /* Always replicate HINCRBYFLOAT as an HSET command with the final value 
 546      * in order to make sure that differences in float pricision or formatting 
 547      * will not create differences in replicas or after an AOF restart. */ 
 548     aux 
= createStringObject("HSET",4); 
 549     rewriteClientCommandArgument(c
,0,aux
); 
 551     rewriteClientCommandArgument(c
,3,new); 
 555 static void addHashFieldToReply(redisClient 
*c
, robj 
*o
, robj 
*field
) { 
 559         addReply(c
, shared
.nullbulk
); 
 563     if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 564         unsigned char *vstr 
= NULL
; 
 565         unsigned int vlen 
= UINT_MAX
; 
 566         long long vll 
= LLONG_MAX
; 
 568         ret 
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
); 
 570             addReply(c
, shared
.nullbulk
); 
 573                 addReplyBulkCBuffer(c
, vstr
, vlen
); 
 575                 addReplyBulkLongLong(c
, vll
); 
 579     } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 582         ret 
= hashTypeGetFromHashTable(o
, field
, &value
); 
 584             addReply(c
, shared
.nullbulk
); 
 586             addReplyBulk(c
, value
); 
 590         redisPanic("Unknown hash encoding"); 
 594 void hgetCommand(redisClient 
*c
) { 
 597     if ((o 
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL 
|| 
 598         checkType(c
,o
,REDIS_HASH
)) return; 
 600     addHashFieldToReply(c
, o
, c
->argv
[2]); 
 603 void hmgetCommand(redisClient 
*c
) { 
 607     /* Don't abort when the key cannot be found. Non-existing keys are empty 
 608      * hashes, where HMGET should respond with a series of null bulks. */ 
 609     o 
= lookupKeyRead(c
->db
, c
->argv
[1]); 
 610     if (o 
!= NULL 
&& o
->type 
!= REDIS_HASH
) { 
 611         addReply(c
, shared
.wrongtypeerr
); 
 615     addReplyMultiBulkLen(c
, c
->argc
-2); 
 616     for (i 
= 2; i 
< c
->argc
; i
++) { 
 617         addHashFieldToReply(c
, o
, c
->argv
[i
]); 
 621 void hdelCommand(redisClient 
*c
) { 
 625     if ((o 
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL 
|| 
 626         checkType(c
,o
,REDIS_HASH
)) return; 
 628     for (j 
= 2; j 
< c
->argc
; j
++) { 
 629         if (hashTypeDelete(o
,c
->argv
[j
])) { 
 631             if (hashTypeLength(o
) == 0) { 
 632                 dbDelete(c
->db
,c
->argv
[1]); 
 638         signalModifiedKey(c
->db
,c
->argv
[1]); 
 639         server
.dirty 
+= deleted
; 
 641     addReplyLongLong(c
,deleted
); 
 644 void hlenCommand(redisClient 
*c
) { 
 646     if ((o 
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL 
|| 
 647         checkType(c
,o
,REDIS_HASH
)) return; 
 649     addReplyLongLong(c
,hashTypeLength(o
)); 
 652 static void addHashIteratorCursorToReply(redisClient 
*c
, hashTypeIterator 
*hi
, int what
) { 
 653     if (hi
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 654         unsigned char *vstr 
= NULL
; 
 655         unsigned int vlen 
= UINT_MAX
; 
 656         long long vll 
= LLONG_MAX
; 
 658         hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
); 
 660             addReplyBulkCBuffer(c
, vstr
, vlen
); 
 662             addReplyBulkLongLong(c
, vll
); 
 665     } else if (hi
->encoding 
== REDIS_ENCODING_HT
) { 
 668         hashTypeCurrentFromHashTable(hi
, what
, &value
); 
 669         addReplyBulk(c
, value
); 
 672         redisPanic("Unknown hash encoding"); 
 676 void genericHgetallCommand(redisClient 
*c
, int flags
) { 
 678     hashTypeIterator 
*hi
; 
 680     int length
, count 
= 0; 
 682     if ((o 
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
 
 683         || checkType(c
,o
,REDIS_HASH
)) return; 
 685     if (flags 
& REDIS_HASH_KEY
) multiplier
++; 
 686     if (flags 
& REDIS_HASH_VALUE
) multiplier
++; 
 688     length 
= hashTypeLength(o
) * multiplier
; 
 689     addReplyMultiBulkLen(c
, length
); 
 691     hi 
= hashTypeInitIterator(o
); 
 692     while (hashTypeNext(hi
) != REDIS_ERR
) { 
 693         if (flags 
& REDIS_HASH_KEY
) { 
 694             addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
); 
 697         if (flags 
& REDIS_HASH_VALUE
) { 
 698             addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
); 
 703     hashTypeReleaseIterator(hi
); 
 704     redisAssert(count 
== length
); 
 707 void hkeysCommand(redisClient 
*c
) { 
 708     genericHgetallCommand(c
,REDIS_HASH_KEY
); 
 711 void hvalsCommand(redisClient 
*c
) { 
 712     genericHgetallCommand(c
,REDIS_HASH_VALUE
); 
 715 void hgetallCommand(redisClient 
*c
) { 
 716     genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
); 
 719 void hexistsCommand(redisClient 
*c
) { 
 721     if ((o 
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL 
|| 
 722         checkType(c
,o
,REDIS_HASH
)) return; 
 724     addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone 
: shared
.czero
);