]>
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
);
82 *value
= dictGetVal(de
);
86 /* Higher level function of hashTypeGet*() that always returns a Redis
87 * object (either new or with refcount incremented), so that the caller
88 * can retain a reference or call decrRefCount after the usage.
90 * The lower level function can prevent copy on write so it is
91 * the preferred way of doing read operations. */
92 robj
*hashTypeGetObject(robj
*o
, robj
*field
) {
95 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
96 unsigned char *vstr
= NULL
;
97 unsigned int vlen
= UINT_MAX
;
98 long long vll
= LLONG_MAX
;
100 if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) {
102 value
= createStringObject((char*)vstr
, vlen
);
104 value
= createStringObjectFromLongLong(vll
);
108 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
111 if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) {
117 redisPanic("Unknown hash encoding");
123 /* Test if the specified field exists in the given hash. Returns 1 if the field
124 * exists, and 0 when it doesn't. */
125 int hashTypeExists(robj
*o
, robj
*field
) {
126 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
127 unsigned char *vstr
= NULL
;
128 unsigned int vlen
= UINT_MAX
;
129 long long vll
= LLONG_MAX
;
131 if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) {
135 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
138 if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) {
143 redisPanic("Unknown hash encoding");
149 /* Add an element, discard the old if the key already exists.
150 * Return 0 on insert and 1 on update. */
151 int hashTypeSet(robj
*o
, robj
*field
, robj
*value
) {
154 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
155 unsigned char *zl
, *fptr
, *vptr
;
157 field
= getDecodedObject(field
);
158 value
= getDecodedObject(value
);
161 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
163 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
165 /* Grab pointer to the value (fptr points to the field) */
166 vptr
= ziplistNext(zl
, fptr
);
167 redisAssert(vptr
!= NULL
);
171 zl
= ziplistDelete(zl
, &vptr
);
173 /* Insert new value */
174 zl
= ziplistInsert(zl
, vptr
, value
->ptr
, sdslen(value
->ptr
));
179 /* Push new field/value pair onto the tail of the ziplist */
180 zl
= ziplistPush(zl
, field
->ptr
, sdslen(field
->ptr
), ZIPLIST_TAIL
);
181 zl
= ziplistPush(zl
, value
->ptr
, sdslen(value
->ptr
), ZIPLIST_TAIL
);
189 /* Check if the ziplist needs to be converted to a hash table */
190 if (hashTypeLength(o
) > server
.hash_max_ziplist_entries
) {
191 hashTypeConvert(o
, REDIS_ENCODING_HT
);
194 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
195 if (dictReplace(o
->ptr
, field
, value
)) { /* Insert */
197 } else { /* Update */
204 redisPanic("Unknown hash encoding");
210 /* Delete an element from a hash.
211 * Return 1 on deleted and 0 on not found. */
212 int hashTypeDelete(robj
*o
, robj
*field
) {
215 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
216 unsigned char *zl
, *fptr
;
218 field
= getDecodedObject(field
);
221 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
223 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
225 zl
= ziplistDelete(zl
,&fptr
);
226 zl
= ziplistDelete(zl
,&fptr
);
234 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
235 if (dictDelete((dict
*)o
->ptr
, field
) == REDIS_OK
) {
238 /* Always check if the dictionary needs a resize after a delete. */
239 if (htNeedsResize(o
->ptr
)) dictResize(o
->ptr
);
243 redisPanic("Unknown hash encoding");
249 /* Return the number of elements in a hash. */
250 unsigned long hashTypeLength(robj
*o
) {
251 unsigned long length
= ULONG_MAX
;
253 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
254 length
= ziplistLen(o
->ptr
) / 2;
255 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
256 length
= dictSize((dict
*)o
->ptr
);
258 redisPanic("Unknown hash encoding");
264 hashTypeIterator
*hashTypeInitIterator(robj
*subject
) {
265 hashTypeIterator
*hi
= zmalloc(sizeof(hashTypeIterator
));
266 hi
->subject
= subject
;
267 hi
->encoding
= subject
->encoding
;
269 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
272 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
273 hi
->di
= dictGetIterator(subject
->ptr
);
275 redisPanic("Unknown hash encoding");
281 void hashTypeReleaseIterator(hashTypeIterator
*hi
) {
282 if (hi
->encoding
== REDIS_ENCODING_HT
) {
283 dictReleaseIterator(hi
->di
);
289 /* Move to the next entry in the hash. Return REDIS_OK when the next entry
290 * could be found and REDIS_ERR when the iterator reaches the end. */
291 int hashTypeNext(hashTypeIterator
*hi
) {
292 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
294 unsigned char *fptr
, *vptr
;
296 zl
= hi
->subject
->ptr
;
301 /* Initialize cursor */
302 redisAssert(vptr
== NULL
);
303 fptr
= ziplistIndex(zl
, 0);
306 redisAssert(vptr
!= NULL
);
307 fptr
= ziplistNext(zl
, vptr
);
314 /* Grab pointer to the value (fptr points to the field) */
315 vptr
= ziplistNext(zl
, fptr
);
316 redisAssert(vptr
!= NULL
);
318 /* fptr, vptr now point to the first or next pair */
322 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
323 if ((hi
->de
= dictNext(hi
->di
)) == NULL
) {
328 redisPanic("Unknown hash encoding");
334 /* Get the field or value at iterator cursor, for an iterator on a hash value
335 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
336 void hashTypeCurrentFromZiplist(hashTypeIterator
*hi
, int what
,
337 unsigned char **vstr
,
343 redisAssert(hi
->encoding
== REDIS_ENCODING_ZIPLIST
);
345 if (what
& REDIS_HASH_KEY
) {
346 ret
= ziplistGet(hi
->fptr
, vstr
, vlen
, vll
);
349 ret
= ziplistGet(hi
->vptr
, vstr
, vlen
, vll
);
354 /* Get the field or value at iterator cursor, for an iterator on a hash value
355 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
356 void hashTypeCurrentFromHashTable(hashTypeIterator
*hi
, int what
, robj
**dst
) {
357 redisAssert(hi
->encoding
== REDIS_ENCODING_HT
);
359 if (what
& REDIS_HASH_KEY
) {
360 *dst
= dictGetKey(hi
->de
);
362 *dst
= dictGetVal(hi
->de
);
366 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
367 * that returns an object with incremented refcount (or a new object). It is up
368 * to the caller to decrRefCount() the object if no reference is retained. */
369 robj
*hashTypeCurrentObject(hashTypeIterator
*hi
, int what
) {
372 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
373 unsigned char *vstr
= NULL
;
374 unsigned int vlen
= UINT_MAX
;
375 long long vll
= LLONG_MAX
;
377 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
379 dst
= createStringObject((char*)vstr
, vlen
);
381 dst
= createStringObjectFromLongLong(vll
);
384 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
385 hashTypeCurrentFromHashTable(hi
, what
, &dst
);
389 redisPanic("Unknown hash encoding");
395 robj
*hashTypeLookupWriteOrCreate(redisClient
*c
, robj
*key
) {
396 robj
*o
= lookupKeyWrite(c
->db
,key
);
398 o
= createHashObject();
401 if (o
->type
!= REDIS_HASH
) {
402 addReply(c
,shared
.wrongtypeerr
);
409 void hashTypeConvertZiplist(robj
*o
, int enc
) {
410 redisAssert(o
->encoding
== REDIS_ENCODING_ZIPLIST
);
412 if (enc
== REDIS_ENCODING_ZIPLIST
) {
413 /* Nothing to do... */
415 } else if (enc
== REDIS_ENCODING_HT
) {
416 hashTypeIterator
*hi
;
420 hi
= hashTypeInitIterator(o
);
421 dict
= dictCreate(&hashDictType
, NULL
);
423 while (hashTypeNext(hi
) != REDIS_ERR
) {
426 field
= hashTypeCurrentObject(hi
, REDIS_HASH_KEY
);
427 field
= tryObjectEncoding(field
);
428 value
= hashTypeCurrentObject(hi
, REDIS_HASH_VALUE
);
429 value
= tryObjectEncoding(value
);
430 ret
= dictAdd(dict
, field
, value
);
431 redisAssert(ret
== DICT_OK
);
434 hashTypeReleaseIterator(hi
);
437 o
->encoding
= REDIS_ENCODING_HT
;
441 redisPanic("Unknown hash encoding");
445 void hashTypeConvert(robj
*o
, int enc
) {
446 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
447 hashTypeConvertZiplist(o
, enc
);
448 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
449 redisPanic("Not implemented");
451 redisPanic("Unknown hash encoding");
455 /*-----------------------------------------------------------------------------
457 *----------------------------------------------------------------------------*/
459 void hsetCommand(redisClient
*c
) {
463 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
464 hashTypeTryConversion(o
,c
->argv
,2,3);
465 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
466 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
467 addReply(c
, update
? shared
.czero
: shared
.cone
);
468 signalModifiedKey(c
->db
,c
->argv
[1]);
472 void hsetnxCommand(redisClient
*c
) {
474 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
475 hashTypeTryConversion(o
,c
->argv
,2,3);
477 if (hashTypeExists(o
, c
->argv
[2])) {
478 addReply(c
, shared
.czero
);
480 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
481 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
482 addReply(c
, shared
.cone
);
483 signalModifiedKey(c
->db
,c
->argv
[1]);
488 void hmsetCommand(redisClient
*c
) {
492 if ((c
->argc
% 2) == 1) {
493 addReplyError(c
,"wrong number of arguments for HMSET");
497 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
498 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
499 for (i
= 2; i
< c
->argc
; i
+= 2) {
500 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
501 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
503 addReply(c
, shared
.ok
);
504 signalModifiedKey(c
->db
,c
->argv
[1]);
508 void hincrbyCommand(redisClient
*c
) {
509 long long value
, incr
;
510 robj
*o
, *current
, *new;
512 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
513 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
514 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
515 if (getLongLongFromObjectOrReply(c
,current
,&value
,
516 "hash value is not an integer") != REDIS_OK
) {
517 decrRefCount(current
);
520 decrRefCount(current
);
526 new = createStringObjectFromLongLong(value
);
527 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
528 hashTypeSet(o
,c
->argv
[2],new);
530 addReplyLongLong(c
,value
);
531 signalModifiedKey(c
->db
,c
->argv
[1]);
535 void hincrbyfloatCommand(redisClient
*c
) {
536 double long value
, incr
;
537 robj
*o
, *current
, *new;
539 if (getLongDoubleFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
540 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
541 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
542 if (getLongDoubleFromObjectOrReply(c
,current
,&value
,
543 "hash value is not a valid float") != REDIS_OK
) {
544 decrRefCount(current
);
547 decrRefCount(current
);
553 new = createStringObjectFromLongDouble(value
);
554 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
555 hashTypeSet(o
,c
->argv
[2],new);
558 signalModifiedKey(c
->db
,c
->argv
[1]);
562 static void addHashFieldToReply(redisClient
*c
, robj
*o
, robj
*field
) {
566 addReply(c
, shared
.nullbulk
);
570 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
571 unsigned char *vstr
= NULL
;
572 unsigned int vlen
= UINT_MAX
;
573 long long vll
= LLONG_MAX
;
575 ret
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
);
577 addReply(c
, shared
.nullbulk
);
580 addReplyBulkCBuffer(c
, vstr
, vlen
);
582 addReplyBulkLongLong(c
, vll
);
586 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
589 ret
= hashTypeGetFromHashTable(o
, field
, &value
);
591 addReply(c
, shared
.nullbulk
);
593 addReplyBulk(c
, value
);
597 redisPanic("Unknown hash encoding");
601 void hgetCommand(redisClient
*c
) {
604 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
605 checkType(c
,o
,REDIS_HASH
)) return;
607 addHashFieldToReply(c
, o
, c
->argv
[2]);
610 void hmgetCommand(redisClient
*c
) {
614 /* Don't abort when the key cannot be found. Non-existing keys are empty
615 * hashes, where HMGET should respond with a series of null bulks. */
616 o
= lookupKeyRead(c
->db
, c
->argv
[1]);
617 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
618 addReply(c
, shared
.wrongtypeerr
);
622 addReplyMultiBulkLen(c
, c
->argc
-2);
623 for (i
= 2; i
< c
->argc
; i
++) {
624 addHashFieldToReply(c
, o
, c
->argv
[i
]);
628 void hdelCommand(redisClient
*c
) {
632 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
633 checkType(c
,o
,REDIS_HASH
)) return;
635 for (j
= 2; j
< c
->argc
; j
++) {
636 if (hashTypeDelete(o
,c
->argv
[j
])) {
638 if (hashTypeLength(o
) == 0) {
639 dbDelete(c
->db
,c
->argv
[1]);
645 signalModifiedKey(c
->db
,c
->argv
[1]);
646 server
.dirty
+= deleted
;
648 addReplyLongLong(c
,deleted
);
651 void hlenCommand(redisClient
*c
) {
653 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
654 checkType(c
,o
,REDIS_HASH
)) return;
656 addReplyLongLong(c
,hashTypeLength(o
));
659 static void addHashIteratorCursorToReply(redisClient
*c
, hashTypeIterator
*hi
, int what
) {
660 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
661 unsigned char *vstr
= NULL
;
662 unsigned int vlen
= UINT_MAX
;
663 long long vll
= LLONG_MAX
;
665 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
667 addReplyBulkCBuffer(c
, vstr
, vlen
);
669 addReplyBulkLongLong(c
, vll
);
672 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
675 hashTypeCurrentFromHashTable(hi
, what
, &value
);
676 addReplyBulk(c
, value
);
679 redisPanic("Unknown hash encoding");
683 void genericHgetallCommand(redisClient
*c
, int flags
) {
685 hashTypeIterator
*hi
;
687 int length
, count
= 0;
689 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
690 || checkType(c
,o
,REDIS_HASH
)) return;
692 if (flags
& REDIS_HASH_KEY
) multiplier
++;
693 if (flags
& REDIS_HASH_VALUE
) multiplier
++;
695 length
= hashTypeLength(o
) * multiplier
;
696 addReplyMultiBulkLen(c
, length
);
698 hi
= hashTypeInitIterator(o
);
699 while (hashTypeNext(hi
) != REDIS_ERR
) {
700 if (flags
& REDIS_HASH_KEY
) {
701 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
);
704 if (flags
& REDIS_HASH_VALUE
) {
705 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
);
710 hashTypeReleaseIterator(hi
);
711 redisAssert(count
== length
);
714 void hkeysCommand(redisClient
*c
) {
715 genericHgetallCommand(c
,REDIS_HASH_KEY
);
718 void hvalsCommand(redisClient
*c
) {
719 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
722 void hgetallCommand(redisClient
*c
) {
723 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
726 void hexistsCommand(redisClient
*c
) {
728 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
729 checkType(c
,o
,REDIS_HASH
)) return;
731 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);