]>
git.saurik.com Git - redis.git/blob - src/t_hash.c
b09ff41ea58ba79069dd61edefa0a6486b53b0ef
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) {
114 redisPanic("Unknown hash encoding");
120 /* Test if the specified field exists in the given hash. Returns 1 if the field
121 * exists, and 0 when it doesn't. */
122 int hashTypeExists(robj
*o
, robj
*field
) {
123 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
124 unsigned char *vstr
= NULL
;
125 unsigned int vlen
= UINT_MAX
;
126 long long vll
= LLONG_MAX
;
128 if (hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
) == 0) {
132 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
135 if (hashTypeGetFromHashTable(o
, field
, &aux
) == 0) {
140 redisPanic("Unknown hash encoding");
146 /* Add an element, discard the old if the key already exists.
147 * Return 0 on insert and 1 on update. */
148 int hashTypeSet(robj
*o
, robj
*field
, robj
*value
) {
151 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
152 unsigned char *zl
, *fptr
, *vptr
;
154 field
= getDecodedObject(field
);
155 value
= getDecodedObject(value
);
158 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
160 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
162 /* Grab pointer to the value (fptr points to the field) */
163 vptr
= ziplistNext(zl
, fptr
);
164 redisAssert(vptr
!= NULL
);
168 zl
= ziplistDelete(zl
, &vptr
);
170 /* Insert new value */
171 zl
= ziplistInsert(zl
, vptr
, value
->ptr
, sdslen(value
->ptr
));
176 /* Push new field/value pair onto the tail of the ziplist */
177 zl
= ziplistPush(zl
, field
->ptr
, sdslen(field
->ptr
), ZIPLIST_TAIL
);
178 zl
= ziplistPush(zl
, value
->ptr
, sdslen(value
->ptr
), ZIPLIST_TAIL
);
186 /* Check if the ziplist needs to be converted to a hash table */
187 if (hashTypeLength(o
) > server
.hash_max_ziplist_entries
) {
188 hashTypeConvert(o
, REDIS_ENCODING_HT
);
191 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
192 if (dictReplace(o
->ptr
, field
, value
)) { /* Insert */
194 } else { /* Update */
201 redisPanic("Unknown hash encoding");
207 /* Delete an element from a hash.
208 * Return 1 on deleted and 0 on not found. */
209 int hashTypeDelete(robj
*o
, robj
*field
) {
212 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
213 unsigned char *zl
, *fptr
;
215 field
= getDecodedObject(field
);
218 fptr
= ziplistIndex(zl
, ZIPLIST_HEAD
);
220 fptr
= ziplistFind(fptr
, field
->ptr
, sdslen(field
->ptr
), 1);
222 zl
= ziplistDelete(zl
,&fptr
);
223 zl
= ziplistDelete(zl
,&fptr
);
231 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
232 if (dictDelete((dict
*)o
->ptr
, field
) == REDIS_OK
) {
235 /* Always check if the dictionary needs a resize after a delete. */
236 if (htNeedsResize(o
->ptr
)) dictResize(o
->ptr
);
240 redisPanic("Unknown hash encoding");
246 /* Return the number of elements in a hash. */
247 unsigned long hashTypeLength(robj
*o
) {
248 unsigned long length
= ULONG_MAX
;
250 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
251 length
= ziplistLen(o
->ptr
) / 2;
252 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
253 length
= dictSize((dict
*)o
->ptr
);
255 redisPanic("Unknown hash encoding");
261 hashTypeIterator
*hashTypeInitIterator(robj
*subject
) {
262 hashTypeIterator
*hi
= zmalloc(sizeof(hashTypeIterator
));
263 hi
->subject
= subject
;
264 hi
->encoding
= subject
->encoding
;
266 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
269 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
270 hi
->di
= dictGetIterator(subject
->ptr
);
272 redisPanic("Unknown hash encoding");
278 void hashTypeReleaseIterator(hashTypeIterator
*hi
) {
279 if (hi
->encoding
== REDIS_ENCODING_HT
) {
280 dictReleaseIterator(hi
->di
);
286 /* Move to the next entry in the hash. Return REDIS_OK when the next entry
287 * could be found and REDIS_ERR when the iterator reaches the end. */
288 int hashTypeNext(hashTypeIterator
*hi
) {
289 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
291 unsigned char *fptr
, *vptr
;
293 zl
= hi
->subject
->ptr
;
298 /* Initialize cursor */
299 redisAssert(vptr
== NULL
);
300 fptr
= ziplistIndex(zl
, 0);
303 redisAssert(vptr
!= NULL
);
304 fptr
= ziplistNext(zl
, vptr
);
311 /* Grab pointer to the value (fptr points to the field) */
312 vptr
= ziplistNext(zl
, fptr
);
313 redisAssert(vptr
!= NULL
);
315 /* fptr, vptr now point to the first or next pair */
319 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
320 if ((hi
->de
= dictNext(hi
->di
)) == NULL
) {
325 redisPanic("Unknown hash encoding");
331 /* Get the field or value at iterator cursor, for an iterator on a hash value
332 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromZiplist`. */
333 void hashTypeCurrentFromZiplist(hashTypeIterator
*hi
, int what
,
334 unsigned char **vstr
,
340 redisAssert(hi
->encoding
== REDIS_ENCODING_ZIPLIST
);
342 if (what
& REDIS_HASH_KEY
) {
343 ret
= ziplistGet(hi
->fptr
, vstr
, vlen
, vll
);
346 ret
= ziplistGet(hi
->vptr
, vstr
, vlen
, vll
);
351 /* Get the field or value at iterator cursor, for an iterator on a hash value
352 * encoded as a ziplist. Prototype is similar to `hashTypeGetFromHashTable`. */
353 void hashTypeCurrentFromHashTable(hashTypeIterator
*hi
, int what
, robj
**dst
) {
354 redisAssert(hi
->encoding
== REDIS_ENCODING_HT
);
356 if (what
& REDIS_HASH_KEY
) {
357 *dst
= dictGetKey(hi
->de
);
359 *dst
= dictGetVal(hi
->de
);
363 /* A non copy-on-write friendly but higher level version of hashTypeCurrent*()
364 * that returns an object with incremented refcount (or a new object). It is up
365 * to the caller to decrRefCount() the object if no reference is retained. */
366 robj
*hashTypeCurrentObject(hashTypeIterator
*hi
, int what
) {
369 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
370 unsigned char *vstr
= NULL
;
371 unsigned int vlen
= UINT_MAX
;
372 long long vll
= LLONG_MAX
;
374 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
376 dst
= createStringObject((char*)vstr
, vlen
);
378 dst
= createStringObjectFromLongLong(vll
);
381 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
382 hashTypeCurrentFromHashTable(hi
, what
, &dst
);
386 redisPanic("Unknown hash encoding");
392 robj
*hashTypeLookupWriteOrCreate(redisClient
*c
, robj
*key
) {
393 robj
*o
= lookupKeyWrite(c
->db
,key
);
395 o
= createHashObject();
398 if (o
->type
!= REDIS_HASH
) {
399 addReply(c
,shared
.wrongtypeerr
);
406 void hashTypeConvertZiplist(robj
*o
, int enc
) {
407 redisAssert(o
->encoding
== REDIS_ENCODING_ZIPLIST
);
409 if (enc
== REDIS_ENCODING_ZIPLIST
) {
410 /* Nothing to do... */
412 } else if (enc
== REDIS_ENCODING_HT
) {
413 hashTypeIterator
*hi
;
417 hi
= hashTypeInitIterator(o
);
418 dict
= dictCreate(&hashDictType
, NULL
);
420 while (hashTypeNext(hi
) != REDIS_ERR
) {
423 field
= hashTypeCurrentObject(hi
, REDIS_HASH_KEY
);
424 field
= tryObjectEncoding(field
);
425 value
= hashTypeCurrentObject(hi
, REDIS_HASH_VALUE
);
426 value
= tryObjectEncoding(value
);
427 ret
= dictAdd(dict
, field
, value
);
428 redisAssert(ret
== DICT_OK
);
431 hashTypeReleaseIterator(hi
);
434 o
->encoding
= REDIS_ENCODING_HT
;
438 redisPanic("Unknown hash encoding");
442 void hashTypeConvert(robj
*o
, int enc
) {
443 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
444 hashTypeConvertZiplist(o
, enc
);
445 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
446 redisPanic("Not implemented");
448 redisPanic("Unknown hash encoding");
452 /*-----------------------------------------------------------------------------
454 *----------------------------------------------------------------------------*/
456 void hsetCommand(redisClient
*c
) {
460 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
461 hashTypeTryConversion(o
,c
->argv
,2,3);
462 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
463 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
464 addReply(c
, update
? shared
.czero
: shared
.cone
);
465 signalModifiedKey(c
->db
,c
->argv
[1]);
469 void hsetnxCommand(redisClient
*c
) {
471 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
472 hashTypeTryConversion(o
,c
->argv
,2,3);
474 if (hashTypeExists(o
, c
->argv
[2])) {
475 addReply(c
, shared
.czero
);
477 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
478 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
479 addReply(c
, shared
.cone
);
480 signalModifiedKey(c
->db
,c
->argv
[1]);
485 void hmsetCommand(redisClient
*c
) {
489 if ((c
->argc
% 2) == 1) {
490 addReplyError(c
,"wrong number of arguments for HMSET");
494 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
495 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
496 for (i
= 2; i
< c
->argc
; i
+= 2) {
497 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
498 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
500 addReply(c
, shared
.ok
);
501 signalModifiedKey(c
->db
,c
->argv
[1]);
505 void hincrbyCommand(redisClient
*c
) {
506 long long value
, incr
, oldvalue
;
507 robj
*o
, *current
, *new;
509 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
510 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
511 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
512 if (getLongLongFromObjectOrReply(c
,current
,&value
,
513 "hash value is not an integer") != REDIS_OK
) {
514 decrRefCount(current
);
517 decrRefCount(current
);
523 if ((incr
< 0 && oldvalue
< 0 && incr
< (LLONG_MIN
-oldvalue
)) ||
524 (incr
> 0 && oldvalue
> 0 && incr
> (LLONG_MAX
-oldvalue
))) {
525 addReplyError(c
,"increment or decrement would overflow");
529 new = createStringObjectFromLongLong(value
);
530 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
531 hashTypeSet(o
,c
->argv
[2],new);
533 addReplyLongLong(c
,value
);
534 signalModifiedKey(c
->db
,c
->argv
[1]);
538 void hincrbyfloatCommand(redisClient
*c
) {
539 double long value
, incr
;
540 robj
*o
, *current
, *new;
542 if (getLongDoubleFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
543 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
544 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
545 if (getLongDoubleFromObjectOrReply(c
,current
,&value
,
546 "hash value is not a valid float") != REDIS_OK
) {
547 decrRefCount(current
);
550 decrRefCount(current
);
556 new = createStringObjectFromLongDouble(value
);
557 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
558 hashTypeSet(o
,c
->argv
[2],new);
561 signalModifiedKey(c
->db
,c
->argv
[1]);
565 static void addHashFieldToReply(redisClient
*c
, robj
*o
, robj
*field
) {
569 addReply(c
, shared
.nullbulk
);
573 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
574 unsigned char *vstr
= NULL
;
575 unsigned int vlen
= UINT_MAX
;
576 long long vll
= LLONG_MAX
;
578 ret
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
);
580 addReply(c
, shared
.nullbulk
);
583 addReplyBulkCBuffer(c
, vstr
, vlen
);
585 addReplyBulkLongLong(c
, vll
);
589 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
592 ret
= hashTypeGetFromHashTable(o
, field
, &value
);
594 addReply(c
, shared
.nullbulk
);
596 addReplyBulk(c
, value
);
600 redisPanic("Unknown hash encoding");
604 void hgetCommand(redisClient
*c
) {
607 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
608 checkType(c
,o
,REDIS_HASH
)) return;
610 addHashFieldToReply(c
, o
, c
->argv
[2]);
613 void hmgetCommand(redisClient
*c
) {
617 /* Don't abort when the key cannot be found. Non-existing keys are empty
618 * hashes, where HMGET should respond with a series of null bulks. */
619 o
= lookupKeyRead(c
->db
, c
->argv
[1]);
620 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
621 addReply(c
, shared
.wrongtypeerr
);
625 addReplyMultiBulkLen(c
, c
->argc
-2);
626 for (i
= 2; i
< c
->argc
; i
++) {
627 addHashFieldToReply(c
, o
, c
->argv
[i
]);
631 void hdelCommand(redisClient
*c
) {
635 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
636 checkType(c
,o
,REDIS_HASH
)) return;
638 for (j
= 2; j
< c
->argc
; j
++) {
639 if (hashTypeDelete(o
,c
->argv
[j
])) {
641 if (hashTypeLength(o
) == 0) {
642 dbDelete(c
->db
,c
->argv
[1]);
648 signalModifiedKey(c
->db
,c
->argv
[1]);
649 server
.dirty
+= deleted
;
651 addReplyLongLong(c
,deleted
);
654 void hlenCommand(redisClient
*c
) {
656 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
657 checkType(c
,o
,REDIS_HASH
)) return;
659 addReplyLongLong(c
,hashTypeLength(o
));
662 static void addHashIteratorCursorToReply(redisClient
*c
, hashTypeIterator
*hi
, int what
) {
663 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
664 unsigned char *vstr
= NULL
;
665 unsigned int vlen
= UINT_MAX
;
666 long long vll
= LLONG_MAX
;
668 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
670 addReplyBulkCBuffer(c
, vstr
, vlen
);
672 addReplyBulkLongLong(c
, vll
);
675 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
678 hashTypeCurrentFromHashTable(hi
, what
, &value
);
679 addReplyBulk(c
, value
);
682 redisPanic("Unknown hash encoding");
686 void genericHgetallCommand(redisClient
*c
, int flags
) {
688 hashTypeIterator
*hi
;
690 int length
, count
= 0;
692 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
693 || checkType(c
,o
,REDIS_HASH
)) return;
695 if (flags
& REDIS_HASH_KEY
) multiplier
++;
696 if (flags
& REDIS_HASH_VALUE
) multiplier
++;
698 length
= hashTypeLength(o
) * multiplier
;
699 addReplyMultiBulkLen(c
, length
);
701 hi
= hashTypeInitIterator(o
);
702 while (hashTypeNext(hi
) != REDIS_ERR
) {
703 if (flags
& REDIS_HASH_KEY
) {
704 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
);
707 if (flags
& REDIS_HASH_VALUE
) {
708 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
);
713 hashTypeReleaseIterator(hi
);
714 redisAssert(count
== length
);
717 void hkeysCommand(redisClient
*c
) {
718 genericHgetallCommand(c
,REDIS_HASH_KEY
);
721 void hvalsCommand(redisClient
*c
) {
722 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
725 void hgetallCommand(redisClient
*c
) {
726 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
729 void hexistsCommand(redisClient
*c
) {
731 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
732 checkType(c
,o
,REDIS_HASH
)) return;
734 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);