]>
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
);