]>
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 int hashTypeSet(robj
*o
, robj
*field
, robj
*value
) {
142 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
143 unsigned char *zl
, *fptr
, *vptr
;
145 field
= getDecodedObject(field
);
146 value
= getDecodedObject(value
);
149 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
151 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
153 /* Grab pointer to the value (fptr points to the field) */
154 vptr
= ziplistNext(zl
, fptr
);
155 redisAssert(vptr
!= NULL
);
159 zl
= ziplistDelete(zl
, &vptr
);
161 /* Insert new value */
162 zl
= ziplistInsert(zl
, vptr
, value
->ptr
, sdslen(value
->ptr
));
167 /* Push new field/value pair onto the tail of the ziplist */
168 zl
= ziplistPush(zl
, field
->ptr
, sdslen(field
->ptr
), ZIPLIST_TAIL
);
169 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
);
182 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
183 if (dictReplace(o
->ptr
, field
, value
)) { /* Insert */
185 } else { /* Update */
192 redisPanic("Unknown hash encoding");
198 /* Delete an element from a hash.
199 * Return 1 on deleted and 0 on not found. */
200 int hashTypeDelete(robj
*o
, robj
*field
) {
203 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
204 unsigned char *zl
, *fptr
;
206 field
= getDecodedObject(field
);
209 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
211 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
213 zl
= ziplistDelete(zl
,&fptr
);
214 zl
= ziplistDelete(zl
,&fptr
);
222 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
223 if (dictDelete((dict
*)o
->ptr
, field
) == REDIS_OK
) {
226 /* Always check if the dictionary needs a resize after a delete. */
227 if (htNeedsResize(o
->ptr
)) dictResize(o
->ptr
);
231 redisPanic("Unknown hash encoding");
237 /* Return the number of elements in a hash. */
238 unsigned long hashTypeLength(robj
*o
) {
239 unsigned long length
= ULONG_MAX
;
241 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
242 length
= ziplistLen(o
->ptr
) / 2;
243 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
244 length
= dictSize((dict
*)o
->ptr
);
246 redisPanic("Unknown hash encoding");
252 hashTypeIterator
*hashTypeInitIterator(robj
*subject
) {
253 hashTypeIterator
*hi
= zmalloc(sizeof(hashTypeIterator
));
254 hi
->subject
= subject
;
255 hi
->encoding
= subject
->encoding
;
257 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
260 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
261 hi
->di
= dictGetIterator(subject
->ptr
);
263 redisPanic("Unknown hash encoding");
269 void hashTypeReleaseIterator(hashTypeIterator
*hi
) {
270 if (hi
->encoding
== REDIS_ENCODING_HT
) {
271 dictReleaseIterator(hi
->di
);
277 /* Move to the next entry in the hash. Return REDIS_OK when the next entry
278 * could be found and REDIS_ERR when the iterator reaches the end. */
279 int hashTypeNext(hashTypeIterator
*hi
) {
280 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
282 unsigned char *fptr
, *vptr
;
284 zl
= hi
->subject
->ptr
;
289 /* Initialize cursor */
290 redisAssert(vptr
== NULL
);
291 fptr
= ziplistIndex(zl
, 0);
294 redisAssert(vptr
!= NULL
);
295 fptr
= ziplistNext(zl
, vptr
);
297 if (fptr
== NULL
) return REDIS_ERR
;
299 /* Grab pointer to the value (fptr points to the field) */
300 vptr
= ziplistNext(zl
, fptr
);
301 redisAssert(vptr
!= NULL
);
303 /* fptr, vptr now point to the first or next pair */
306 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
307 if ((hi
->de
= dictNext(hi
->di
)) == NULL
) return REDIS_ERR
;
309 redisPanic("Unknown hash encoding");
314 /* Get the field or value at iterator cursor, for an iterator on a hash value
315 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
316 void hashTypeCurrentFromZiplist(hashTypeIterator
*hi
, int what
,
317 unsigned char **vstr
,
323 redisAssert(hi
->encoding
== REDIS_ENCODING_ZIPLIST
);
325 if (what
& REDIS_HASH_KEY
) {
326 ret
= ziplistGet(hi
->fptr
, vstr
, vlen
, vll
);
329 ret
= ziplistGet(hi
->vptr
, vstr
, vlen
, vll
);
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 `hashTypeGetFromHashTable`. */
336 void hashTypeCurrentFromHashTable(hashTypeIterator
*hi
, int what
, robj
**dst
) {
337 redisAssert(hi
->encoding
== REDIS_ENCODING_HT
);
339 if (what
& REDIS_HASH_KEY
) {
340 *dst
= dictGetKey(hi
->de
);
342 *dst
= dictGetVal(hi
->de
);
346 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
347 * that returns an object with incremented refcount (or a new object). It is up
348 * to the caller to decrRefCount() the object if no reference is retained. */
349 robj
*hashTypeCurrentObject(hashTypeIterator
*hi
, int what
) {
352 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
353 unsigned char *vstr
= NULL
;
354 unsigned int vlen
= UINT_MAX
;
355 long long vll
= LLONG_MAX
;
357 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
359 dst
= createStringObject((char*)vstr
, vlen
);
361 dst
= createStringObjectFromLongLong(vll
);
364 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
365 hashTypeCurrentFromHashTable(hi
, what
, &dst
);
369 redisPanic("Unknown hash encoding");
375 robj
*hashTypeLookupWriteOrCreate(redisClient
*c
, robj
*key
) {
376 robj
*o
= lookupKeyWrite(c
->db
,key
);
378 o
= createHashObject();
381 if (o
->type
!= REDIS_HASH
) {
382 addReply(c
,shared
.wrongtypeerr
);
389 void hashTypeConvertZiplist(robj
*o
, int enc
) {
390 redisAssert(o
->encoding
== REDIS_ENCODING_ZIPLIST
);
392 if (enc
== REDIS_ENCODING_ZIPLIST
) {
393 /* Nothing to do... */
395 } else if (enc
== REDIS_ENCODING_HT
) {
396 hashTypeIterator
*hi
;
400 hi
= hashTypeInitIterator(o
);
401 dict
= dictCreate(&hashDictType
, NULL
);
403 while (hashTypeNext(hi
) != REDIS_ERR
) {
406 field
= hashTypeCurrentObject(hi
, REDIS_HASH_KEY
);
407 field
= tryObjectEncoding(field
);
408 value
= hashTypeCurrentObject(hi
, REDIS_HASH_VALUE
);
409 value
= tryObjectEncoding(value
);
410 ret
= dictAdd(dict
, field
, value
);
411 redisAssert(ret
== DICT_OK
);
414 hashTypeReleaseIterator(hi
);
417 o
->encoding
= REDIS_ENCODING_HT
;
421 redisPanic("Unknown hash encoding");
425 void hashTypeConvert(robj
*o
, int enc
) {
426 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
427 hashTypeConvertZiplist(o
, enc
);
428 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
429 redisPanic("Not implemented");
431 redisPanic("Unknown hash encoding");
435 /*-----------------------------------------------------------------------------
437 *----------------------------------------------------------------------------*/
439 void hsetCommand(redisClient
*c
) {
443 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
444 hashTypeTryConversion(o
,c
->argv
,2,3);
445 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
446 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
447 addReply(c
, update
? shared
.czero
: shared
.cone
);
448 signalModifiedKey(c
->db
,c
->argv
[1]);
452 void hsetnxCommand(redisClient
*c
) {
454 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
455 hashTypeTryConversion(o
,c
->argv
,2,3);
457 if (hashTypeExists(o
, c
->argv
[2])) {
458 addReply(c
, shared
.czero
);
460 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
461 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
462 addReply(c
, shared
.cone
);
463 signalModifiedKey(c
->db
,c
->argv
[1]);
468 void hmsetCommand(redisClient
*c
) {
472 if ((c
->argc
% 2) == 1) {
473 addReplyError(c
,"wrong number of arguments for HMSET");
477 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
478 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
479 for (i
= 2; i
< c
->argc
; i
+= 2) {
480 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
481 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
483 addReply(c
, shared
.ok
);
484 signalModifiedKey(c
->db
,c
->argv
[1]);
488 void hincrbyCommand(redisClient
*c
) {
489 long long value
, incr
, oldvalue
;
490 robj
*o
, *current
, *new;
492 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
493 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
494 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
495 if (getLongLongFromObjectOrReply(c
,current
,&value
,
496 "hash value is not an integer") != REDIS_OK
) {
497 decrRefCount(current
);
500 decrRefCount(current
);
506 if ((incr
< 0 && oldvalue
< 0 && incr
< (LLONG_MIN
-oldvalue
)) ||
507 (incr
> 0 && oldvalue
> 0 && incr
> (LLONG_MAX
-oldvalue
))) {
508 addReplyError(c
,"increment or decrement would overflow");
512 new = createStringObjectFromLongLong(value
);
513 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
514 hashTypeSet(o
,c
->argv
[2],new);
516 addReplyLongLong(c
,value
);
517 signalModifiedKey(c
->db
,c
->argv
[1]);
521 void hincrbyfloatCommand(redisClient
*c
) {
522 double long value
, incr
;
523 robj
*o
, *current
, *new;
525 if (getLongDoubleFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
526 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
527 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
528 if (getLongDoubleFromObjectOrReply(c
,current
,&value
,
529 "hash value is not a valid float") != REDIS_OK
) {
530 decrRefCount(current
);
533 decrRefCount(current
);
539 new = createStringObjectFromLongDouble(value
);
540 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
541 hashTypeSet(o
,c
->argv
[2],new);
544 signalModifiedKey(c
->db
,c
->argv
[1]);
548 static void addHashFieldToReply(redisClient
*c
, robj
*o
, robj
*field
) {
552 addReply(c
, shared
.nullbulk
);
556 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
557 unsigned char *vstr
= NULL
;
558 unsigned int vlen
= UINT_MAX
;
559 long long vll
= LLONG_MAX
;
561 ret
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
);
563 addReply(c
, shared
.nullbulk
);
566 addReplyBulkCBuffer(c
, vstr
, vlen
);
568 addReplyBulkLongLong(c
, vll
);
572 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
575 ret
= hashTypeGetFromHashTable(o
, field
, &value
);
577 addReply(c
, shared
.nullbulk
);
579 addReplyBulk(c
, value
);
583 redisPanic("Unknown hash encoding");
587 void hgetCommand(redisClient
*c
) {
590 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
591 checkType(c
,o
,REDIS_HASH
)) return;
593 addHashFieldToReply(c
, o
, c
->argv
[2]);
596 void hmgetCommand(redisClient
*c
) {
600 /* Don't abort when the key cannot be found. Non-existing keys are empty
601 * hashes, where HMGET should respond with a series of null bulks. */
602 o
= lookupKeyRead(c
->db
, c
->argv
[1]);
603 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
604 addReply(c
, shared
.wrongtypeerr
);
608 addReplyMultiBulkLen(c
, c
->argc
-2);
609 for (i
= 2; i
< c
->argc
; i
++) {
610 addHashFieldToReply(c
, o
, c
->argv
[i
]);
614 void hdelCommand(redisClient
*c
) {
618 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
619 checkType(c
,o
,REDIS_HASH
)) return;
621 for (j
= 2; j
< c
->argc
; j
++) {
622 if (hashTypeDelete(o
,c
->argv
[j
])) {
624 if (hashTypeLength(o
) == 0) {
625 dbDelete(c
->db
,c
->argv
[1]);
631 signalModifiedKey(c
->db
,c
->argv
[1]);
632 server
.dirty
+= deleted
;
634 addReplyLongLong(c
,deleted
);
637 void hlenCommand(redisClient
*c
) {
639 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
640 checkType(c
,o
,REDIS_HASH
)) return;
642 addReplyLongLong(c
,hashTypeLength(o
));
645 static void addHashIteratorCursorToReply(redisClient
*c
, hashTypeIterator
*hi
, int what
) {
646 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
647 unsigned char *vstr
= NULL
;
648 unsigned int vlen
= UINT_MAX
;
649 long long vll
= LLONG_MAX
;
651 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
653 addReplyBulkCBuffer(c
, vstr
, vlen
);
655 addReplyBulkLongLong(c
, vll
);
658 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
661 hashTypeCurrentFromHashTable(hi
, what
, &value
);
662 addReplyBulk(c
, value
);
665 redisPanic("Unknown hash encoding");
669 void genericHgetallCommand(redisClient
*c
, int flags
) {
671 hashTypeIterator
*hi
;
673 int length
, count
= 0;
675 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
676 || checkType(c
,o
,REDIS_HASH
)) return;
678 if (flags
& REDIS_HASH_KEY
) multiplier
++;
679 if (flags
& REDIS_HASH_VALUE
) multiplier
++;
681 length
= hashTypeLength(o
) * multiplier
;
682 addReplyMultiBulkLen(c
, length
);
684 hi
= hashTypeInitIterator(o
);
685 while (hashTypeNext(hi
) != REDIS_ERR
) {
686 if (flags
& REDIS_HASH_KEY
) {
687 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
);
690 if (flags
& REDIS_HASH_VALUE
) {
691 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
);
696 hashTypeReleaseIterator(hi
);
697 redisAssert(count
== length
);
700 void hkeysCommand(redisClient
*c
) {
701 genericHgetallCommand(c
,REDIS_HASH_KEY
);
704 void hvalsCommand(redisClient
*c
) {
705 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
708 void hgetallCommand(redisClient
*c
) {
709 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
712 void hexistsCommand(redisClient
*c
) {
714 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
715 checkType(c
,o
,REDIS_HASH
)) return;
717 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);