]>
git.saurik.com Git - redis.git/blob - src/t_hash.c
2 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
33 /*-----------------------------------------------------------------------------
35 *----------------------------------------------------------------------------*/
37 /* Check the length of a number of objects to see if we need to convert a
38 * ziplist to a real hash. Note that we only check string encoded objects
39 * as their string length can be queried in constant time. */
40 void hashTypeTryConversion(robj
*o
, robj
**argv
, int start
, int end
) {
43 if (o
->encoding
!= REDIS_ENCODING_ZIPLIST
) return;
45 for (i
= start
; i
<= end
; i
++) {
46 if (argv
[i
]->encoding
== REDIS_ENCODING_RAW
&&
47 sdslen(argv
[i
]->ptr
) > server
.hash_max_ziplist_value
)
49 hashTypeConvert(o
, REDIS_ENCODING_HT
);
55 /* Encode given objects in-place when the hash uses a dict. */
56 void hashTypeTryObjectEncoding(robj
*subject
, robj
**o1
, robj
**o2
) {
57 if (subject
->encoding
== REDIS_ENCODING_HT
) {
58 if (o1
) *o1
= tryObjectEncoding(*o1
);
59 if (o2
) *o2
= tryObjectEncoding(*o2
);
63 /* Get the value from a ziplist encoded hash, identified by field.
64 * Returns -1 when the field cannot be found. */
65 int hashTypeGetFromZiplist(robj
*o
, robj
*field
,
70 unsigned char *zl
, *fptr
= NULL
, *vptr
= NULL
;
73 redisAssert(o
->encoding
== REDIS_ENCODING_ZIPLIST
);
75 field
= getDecodedObject(field
);
78 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
80 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
82 /* Grab pointer to the value (fptr points to the field) */
83 vptr
= ziplistNext(zl
, fptr
);
84 redisAssert(vptr
!= NULL
);
91 ret
= ziplistGet(vptr
, vstr
, vlen
, vll
);
99 /* Get the value from a hash table encoded hash, identified by field.
100 * Returns -1 when the field cannot be found. */
101 int hashTypeGetFromHashTable(robj
*o
, robj
*field
, robj
**value
) {
104 redisAssert(o
->encoding
== REDIS_ENCODING_HT
);
106 de
= dictFind(o
->ptr
, field
);
107 if (de
== NULL
) return -1;
108 *value
= dictGetVal(de
);
112 /* Higher level function of hashTypeGet*() that always returns a Redis
113 * object (either new or with refcount incremented), so that the caller
114 * can retain a reference or call decrRefCount after the usage.
116 * The lower level function can prevent copy on write so it is
117 * the preferred way of doing read operations. */
118 robj
*hashTypeGetObject(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) {
128 value
= createStringObject((char*)vstr
, vlen
);
130 value
= createStringObjectFromLongLong(vll
);
134 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
137 if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) {
142 redisPanic("Unknown hash encoding");
147 /* Test if the specified field exists in the given hash. Returns 1 if the field
148 * exists, and 0 when it doesn't. */
149 int hashTypeExists(robj
*o
, robj
*field
) {
150 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
151 unsigned char *vstr
= NULL
;
152 unsigned int vlen
= UINT_MAX
;
153 long long vll
= LLONG_MAX
;
155 if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) return 1;
156 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
159 if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) return 1;
161 redisPanic("Unknown hash encoding");
166 /* Add an element, discard the old if the key already exists.
167 * Return 0 on insert and 1 on update.
168 * This function will take care of incrementing the reference count of the
169 * retained fields and value objects. */
170 int hashTypeSet(robj
*o
, robj
*field
, robj
*value
) {
173 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
174 unsigned char *zl
, *fptr
, *vptr
;
176 field
= getDecodedObject(field
);
177 value
= getDecodedObject(value
);
180 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
182 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
184 /* Grab pointer to the value (fptr points to the field) */
185 vptr
= ziplistNext(zl
, fptr
);
186 redisAssert(vptr
!= NULL
);
190 zl
= ziplistDelete(zl
, &vptr
);
192 /* Insert new value */
193 zl
= ziplistInsert(zl
, vptr
, value
->ptr
, sdslen(value
->ptr
));
198 /* Push new field/value pair onto the tail of the ziplist */
199 zl
= ziplistPush(zl
, field
->ptr
, sdslen(field
->ptr
), ZIPLIST_TAIL
);
200 zl
= ziplistPush(zl
, value
->ptr
, sdslen(value
->ptr
), ZIPLIST_TAIL
);
206 /* Check if the ziplist needs to be converted to a hash table */
207 if (hashTypeLength(o
) > server
.hash_max_ziplist_entries
)
208 hashTypeConvert(o
, REDIS_ENCODING_HT
);
209 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
210 if (dictReplace(o
->ptr
, field
, value
)) { /* Insert */
212 } else { /* Update */
217 redisPanic("Unknown hash encoding");
222 /* Delete an element from a hash.
223 * Return 1 on deleted and 0 on not found. */
224 int hashTypeDelete(robj
*o
, robj
*field
) {
227 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
228 unsigned char *zl
, *fptr
;
230 field
= getDecodedObject(field
);
233 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
235 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
237 zl
= ziplistDelete(zl
,&fptr
);
238 zl
= ziplistDelete(zl
,&fptr
);
246 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
247 if (dictDelete((dict
*)o
->ptr
, field
) == REDIS_OK
) {
250 /* Always check if the dictionary needs a resize after a delete. */
251 if (htNeedsResize(o
->ptr
)) dictResize(o
->ptr
);
255 redisPanic("Unknown hash encoding");
261 /* Return the number of elements in a hash. */
262 unsigned long hashTypeLength(robj
*o
) {
263 unsigned long length
= ULONG_MAX
;
265 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
266 length
= ziplistLen(o
->ptr
) / 2;
267 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
268 length
= dictSize((dict
*)o
->ptr
);
270 redisPanic("Unknown hash encoding");
276 hashTypeIterator
*hashTypeInitIterator(robj
*subject
) {
277 hashTypeIterator
*hi
= zmalloc(sizeof(hashTypeIterator
));
278 hi
->subject
= subject
;
279 hi
->encoding
= subject
->encoding
;
281 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
284 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
285 hi
->di
= dictGetIterator(subject
->ptr
);
287 redisPanic("Unknown hash encoding");
293 void hashTypeReleaseIterator(hashTypeIterator
*hi
) {
294 if (hi
->encoding
== REDIS_ENCODING_HT
) {
295 dictReleaseIterator(hi
->di
);
301 /* Move to the next entry in the hash. Return REDIS_OK when the next entry
302 * could be found and REDIS_ERR when the iterator reaches the end. */
303 int hashTypeNext(hashTypeIterator
*hi
) {
304 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
306 unsigned char *fptr
, *vptr
;
308 zl
= hi
->subject
->ptr
;
313 /* Initialize cursor */
314 redisAssert(vptr
== NULL
);
315 fptr
= ziplistIndex(zl
, 0);
318 redisAssert(vptr
!= NULL
);
319 fptr
= ziplistNext(zl
, vptr
);
321 if (fptr
== NULL
) return REDIS_ERR
;
323 /* Grab pointer to the value (fptr points to the field) */
324 vptr
= ziplistNext(zl
, fptr
);
325 redisAssert(vptr
!= NULL
);
327 /* fptr, vptr now point to the first or next pair */
330 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
331 if ((hi
->de
= dictNext(hi
->di
)) == NULL
) return REDIS_ERR
;
333 redisPanic("Unknown hash encoding");
338 /* Get the field or value at iterator cursor, for an iterator on a hash value
339 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
340 void hashTypeCurrentFromZiplist(hashTypeIterator
*hi
, int what
,
341 unsigned char **vstr
,
347 redisAssert(hi
->encoding
== REDIS_ENCODING_ZIPLIST
);
349 if (what
& REDIS_HASH_KEY
) {
350 ret
= ziplistGet(hi
->fptr
, vstr
, vlen
, vll
);
353 ret
= ziplistGet(hi
->vptr
, vstr
, vlen
, vll
);
358 /* Get the field or value at iterator cursor, for an iterator on a hash value
359 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
360 void hashTypeCurrentFromHashTable(hashTypeIterator
*hi
, int what
, robj
**dst
) {
361 redisAssert(hi
->encoding
== REDIS_ENCODING_HT
);
363 if (what
& REDIS_HASH_KEY
) {
364 *dst
= dictGetKey(hi
->de
);
366 *dst
= dictGetVal(hi
->de
);
370 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
371 * that returns an object with incremented refcount (or a new object). It is up
372 * to the caller to decrRefCount() the object if no reference is retained. */
373 robj
*hashTypeCurrentObject(hashTypeIterator
*hi
, int what
) {
376 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
377 unsigned char *vstr
= NULL
;
378 unsigned int vlen
= UINT_MAX
;
379 long long vll
= LLONG_MAX
;
381 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
383 dst
= createStringObject((char*)vstr
, vlen
);
385 dst
= createStringObjectFromLongLong(vll
);
388 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
389 hashTypeCurrentFromHashTable(hi
, what
, &dst
);
393 redisPanic("Unknown hash encoding");
399 robj
*hashTypeLookupWriteOrCreate(redisClient
*c
, robj
*key
) {
400 robj
*o
= lookupKeyWrite(c
->db
,key
);
402 o
= createHashObject();
405 if (o
->type
!= REDIS_HASH
) {
406 addReply(c
,shared
.wrongtypeerr
);
413 void hashTypeConvertZiplist(robj
*o
, int enc
) {
414 redisAssert(o
->encoding
== REDIS_ENCODING_ZIPLIST
);
416 if (enc
== REDIS_ENCODING_ZIPLIST
) {
417 /* Nothing to do... */
419 } else if (enc
== REDIS_ENCODING_HT
) {
420 hashTypeIterator
*hi
;
424 hi
= hashTypeInitIterator(o
);
425 dict
= dictCreate(&hashDictType
, NULL
);
427 while (hashTypeNext(hi
) != REDIS_ERR
) {
430 field
= hashTypeCurrentObject(hi
, REDIS_HASH_KEY
);
431 field
= tryObjectEncoding(field
);
432 value
= hashTypeCurrentObject(hi
, REDIS_HASH_VALUE
);
433 value
= tryObjectEncoding(value
);
434 ret
= dictAdd(dict
, field
, value
);
435 if (ret
!= DICT_OK
) {
436 redisLogHexDump(REDIS_WARNING
,"ziplist with dup elements dump",
437 o
->ptr
,ziplistBlobLen(o
->ptr
));
438 redisAssert(ret
== DICT_OK
);
442 hashTypeReleaseIterator(hi
);
445 o
->encoding
= REDIS_ENCODING_HT
;
449 redisPanic("Unknown hash encoding");
453 void hashTypeConvert(robj
*o
, int enc
) {
454 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
455 hashTypeConvertZiplist(o
, enc
);
456 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
457 redisPanic("Not implemented");
459 redisPanic("Unknown hash encoding");
463 /*-----------------------------------------------------------------------------
465 *----------------------------------------------------------------------------*/
467 void hsetCommand(redisClient
*c
) {
471 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
472 hashTypeTryConversion(o
,c
->argv
,2,3);
473 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
474 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
475 addReply(c
, update
? shared
.czero
: shared
.cone
);
476 signalModifiedKey(c
->db
,c
->argv
[1]);
480 void hsetnxCommand(redisClient
*c
) {
482 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
483 hashTypeTryConversion(o
,c
->argv
,2,3);
485 if (hashTypeExists(o
, c
->argv
[2])) {
486 addReply(c
, shared
.czero
);
488 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
489 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
490 addReply(c
, shared
.cone
);
491 signalModifiedKey(c
->db
,c
->argv
[1]);
496 void hmsetCommand(redisClient
*c
) {
500 if ((c
->argc
% 2) == 1) {
501 addReplyError(c
,"wrong number of arguments for HMSET");
505 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
506 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
507 for (i
= 2; i
< c
->argc
; i
+= 2) {
508 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
509 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
511 addReply(c
, shared
.ok
);
512 signalModifiedKey(c
->db
,c
->argv
[1]);
516 void hincrbyCommand(redisClient
*c
) {
517 long long value
, incr
, oldvalue
;
518 robj
*o
, *current
, *new;
520 if (getLongLongFromObjectOrReply(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 (getLongLongFromObjectOrReply(c
,current
,&value
,
524 "hash value is not an integer") != REDIS_OK
) {
525 decrRefCount(current
);
528 decrRefCount(current
);
534 if ((incr
< 0 && oldvalue
< 0 && incr
< (LLONG_MIN
-oldvalue
)) ||
535 (incr
> 0 && oldvalue
> 0 && incr
> (LLONG_MAX
-oldvalue
))) {
536 addReplyError(c
,"increment or decrement would overflow");
540 new = createStringObjectFromLongLong(value
);
541 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
542 hashTypeSet(o
,c
->argv
[2],new);
544 addReplyLongLong(c
,value
);
545 signalModifiedKey(c
->db
,c
->argv
[1]);
549 void hincrbyfloatCommand(redisClient
*c
) {
550 double long value
, incr
;
551 robj
*o
, *current
, *new, *aux
;
553 if (getLongDoubleFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
554 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
555 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
556 if (getLongDoubleFromObjectOrReply(c
,current
,&value
,
557 "hash value is not a valid float") != REDIS_OK
) {
558 decrRefCount(current
);
561 decrRefCount(current
);
567 new = createStringObjectFromLongDouble(value
);
568 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
569 hashTypeSet(o
,c
->argv
[2],new);
571 signalModifiedKey(c
->db
,c
->argv
[1]);
574 /* Always replicate HINCRBYFLOAT as an HSET command with the final value
575 * in order to make sure that differences in float pricision or formatting
576 * will not create differences in replicas or after an AOF restart. */
577 aux
= createStringObject("HSET",4);
578 rewriteClientCommandArgument(c
,0,aux
);
580 rewriteClientCommandArgument(c
,3,new);
584 static void addHashFieldToReply(redisClient
*c
, robj
*o
, robj
*field
) {
588 addReply(c
, shared
.nullbulk
);
592 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
593 unsigned char *vstr
= NULL
;
594 unsigned int vlen
= UINT_MAX
;
595 long long vll
= LLONG_MAX
;
597 ret
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
);
599 addReply(c
, shared
.nullbulk
);
602 addReplyBulkCBuffer(c
, vstr
, vlen
);
604 addReplyBulkLongLong(c
, vll
);
608 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
611 ret
= hashTypeGetFromHashTable(o
, field
, &value
);
613 addReply(c
, shared
.nullbulk
);
615 addReplyBulk(c
, value
);
619 redisPanic("Unknown hash encoding");
623 void hgetCommand(redisClient
*c
) {
626 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
627 checkType(c
,o
,REDIS_HASH
)) return;
629 addHashFieldToReply(c
, o
, c
->argv
[2]);
632 void hmgetCommand(redisClient
*c
) {
636 /* Don't abort when the key cannot be found. Non-existing keys are empty
637 * hashes, where HMGET should respond with a series of null bulks. */
638 o
= lookupKeyRead(c
->db
, c
->argv
[1]);
639 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
640 addReply(c
, shared
.wrongtypeerr
);
644 addReplyMultiBulkLen(c
, c
->argc
-2);
645 for (i
= 2; i
< c
->argc
; i
++) {
646 addHashFieldToReply(c
, o
, c
->argv
[i
]);
650 void hdelCommand(redisClient
*c
) {
654 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
655 checkType(c
,o
,REDIS_HASH
)) return;
657 for (j
= 2; j
< c
->argc
; j
++) {
658 if (hashTypeDelete(o
,c
->argv
[j
])) {
660 if (hashTypeLength(o
) == 0) {
661 dbDelete(c
->db
,c
->argv
[1]);
667 signalModifiedKey(c
->db
,c
->argv
[1]);
668 server
.dirty
+= deleted
;
670 addReplyLongLong(c
,deleted
);
673 void hlenCommand(redisClient
*c
) {
675 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
676 checkType(c
,o
,REDIS_HASH
)) return;
678 addReplyLongLong(c
,hashTypeLength(o
));
681 static void addHashIteratorCursorToReply(redisClient
*c
, hashTypeIterator
*hi
, int what
) {
682 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
683 unsigned char *vstr
= NULL
;
684 unsigned int vlen
= UINT_MAX
;
685 long long vll
= LLONG_MAX
;
687 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
689 addReplyBulkCBuffer(c
, vstr
, vlen
);
691 addReplyBulkLongLong(c
, vll
);
694 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
697 hashTypeCurrentFromHashTable(hi
, what
, &value
);
698 addReplyBulk(c
, value
);
701 redisPanic("Unknown hash encoding");
705 void genericHgetallCommand(redisClient
*c
, int flags
) {
707 hashTypeIterator
*hi
;
709 int length
, count
= 0;
711 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
712 || checkType(c
,o
,REDIS_HASH
)) return;
714 if (flags
& REDIS_HASH_KEY
) multiplier
++;
715 if (flags
& REDIS_HASH_VALUE
) multiplier
++;
717 length
= hashTypeLength(o
) * multiplier
;
718 addReplyMultiBulkLen(c
, length
);
720 hi
= hashTypeInitIterator(o
);
721 while (hashTypeNext(hi
) != REDIS_ERR
) {
722 if (flags
& REDIS_HASH_KEY
) {
723 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
);
726 if (flags
& REDIS_HASH_VALUE
) {
727 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
);
732 hashTypeReleaseIterator(hi
);
733 redisAssert(count
== length
);
736 void hkeysCommand(redisClient
*c
) {
737 genericHgetallCommand(c
,REDIS_HASH_KEY
);
740 void hvalsCommand(redisClient
*c
) {
741 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
744 void hgetallCommand(redisClient
*c
) {
745 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
748 void hexistsCommand(redisClient
*c
) {
750 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
751 checkType(c
,o
,REDIS_HASH
)) return;
753 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);