]>
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 redisAssert(ret
== DICT_OK
);
409 hashTypeReleaseIterator(hi
);
412 o
->encoding
= REDIS_ENCODING_HT
;
416 redisPanic("Unknown hash encoding");
420 void hashTypeConvert(robj
*o
, int enc
) {
421 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
422 hashTypeConvertZiplist(o
, enc
);
423 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
424 redisPanic("Not implemented");
426 redisPanic("Unknown hash encoding");
430 /*-----------------------------------------------------------------------------
432 *----------------------------------------------------------------------------*/
434 void hsetCommand(redisClient
*c
) {
438 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
439 hashTypeTryConversion(o
,c
->argv
,2,3);
440 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
441 update
= hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
442 addReply(c
, update
? shared
.czero
: shared
.cone
);
443 signalModifiedKey(c
->db
,c
->argv
[1]);
447 void hsetnxCommand(redisClient
*c
) {
449 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
450 hashTypeTryConversion(o
,c
->argv
,2,3);
452 if (hashTypeExists(o
, c
->argv
[2])) {
453 addReply(c
, shared
.czero
);
455 hashTypeTryObjectEncoding(o
,&c
->argv
[2], &c
->argv
[3]);
456 hashTypeSet(o
,c
->argv
[2],c
->argv
[3]);
457 addReply(c
, shared
.cone
);
458 signalModifiedKey(c
->db
,c
->argv
[1]);
463 void hmsetCommand(redisClient
*c
) {
467 if ((c
->argc
% 2) == 1) {
468 addReplyError(c
,"wrong number of arguments for HMSET");
472 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
473 hashTypeTryConversion(o
,c
->argv
,2,c
->argc
-1);
474 for (i
= 2; i
< c
->argc
; i
+= 2) {
475 hashTypeTryObjectEncoding(o
,&c
->argv
[i
], &c
->argv
[i
+1]);
476 hashTypeSet(o
,c
->argv
[i
],c
->argv
[i
+1]);
478 addReply(c
, shared
.ok
);
479 signalModifiedKey(c
->db
,c
->argv
[1]);
483 void hincrbyCommand(redisClient
*c
) {
484 long long value
, incr
, oldvalue
;
485 robj
*o
, *current
, *new;
487 if (getLongLongFromObjectOrReply(c
,c
->argv
[3],&incr
,NULL
) != REDIS_OK
) return;
488 if ((o
= hashTypeLookupWriteOrCreate(c
,c
->argv
[1])) == NULL
) return;
489 if ((current
= hashTypeGetObject(o
,c
->argv
[2])) != NULL
) {
490 if (getLongLongFromObjectOrReply(c
,current
,&value
,
491 "hash value is not an integer") != REDIS_OK
) {
492 decrRefCount(current
);
495 decrRefCount(current
);
501 if ((incr
< 0 && oldvalue
< 0 && incr
< (LLONG_MIN
-oldvalue
)) ||
502 (incr
> 0 && oldvalue
> 0 && incr
> (LLONG_MAX
-oldvalue
))) {
503 addReplyError(c
,"increment or decrement would overflow");
507 new = createStringObjectFromLongLong(value
);
508 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
509 hashTypeSet(o
,c
->argv
[2],new);
511 addReplyLongLong(c
,value
);
512 signalModifiedKey(c
->db
,c
->argv
[1]);
516 void hincrbyfloatCommand(redisClient
*c
) {
517 double long value
, incr
;
518 robj
*o
, *current
, *new, *aux
;
520 if (getLongDoubleFromObjectOrReply(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 (getLongDoubleFromObjectOrReply(c
,current
,&value
,
524 "hash value is not a valid float") != REDIS_OK
) {
525 decrRefCount(current
);
528 decrRefCount(current
);
534 new = createStringObjectFromLongDouble(value
);
535 hashTypeTryObjectEncoding(o
,&c
->argv
[2],NULL
);
536 hashTypeSet(o
,c
->argv
[2],new);
538 signalModifiedKey(c
->db
,c
->argv
[1]);
541 /* Always replicate HINCRBYFLOAT as an HSET command with the final value
542 * in order to make sure that differences in float pricision or formatting
543 * will not create differences in replicas or after an AOF restart. */
544 aux
= createStringObject("HSET",4);
545 rewriteClientCommandArgument(c
,0,aux
);
547 rewriteClientCommandArgument(c
,3,new);
551 static void addHashFieldToReply(redisClient
*c
, robj
*o
, robj
*field
) {
555 addReply(c
, shared
.nullbulk
);
559 if (o
->encoding
== REDIS_ENCODING_ZIPLIST
) {
560 unsigned char *vstr
= NULL
;
561 unsigned int vlen
= UINT_MAX
;
562 long long vll
= LLONG_MAX
;
564 ret
= hashTypeGetFromZiplist(o
, field
, &vstr
, &vlen
, &vll
);
566 addReply(c
, shared
.nullbulk
);
569 addReplyBulkCBuffer(c
, vstr
, vlen
);
571 addReplyBulkLongLong(c
, vll
);
575 } else if (o
->encoding
== REDIS_ENCODING_HT
) {
578 ret
= hashTypeGetFromHashTable(o
, field
, &value
);
580 addReply(c
, shared
.nullbulk
);
582 addReplyBulk(c
, value
);
586 redisPanic("Unknown hash encoding");
590 void hgetCommand(redisClient
*c
) {
593 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.nullbulk
)) == NULL
||
594 checkType(c
,o
,REDIS_HASH
)) return;
596 addHashFieldToReply(c
, o
, c
->argv
[2]);
599 void hmgetCommand(redisClient
*c
) {
603 /* Don't abort when the key cannot be found. Non-existing keys are empty
604 * hashes, where HMGET should respond with a series of null bulks. */
605 o
= lookupKeyRead(c
->db
, c
->argv
[1]);
606 if (o
!= NULL
&& o
->type
!= REDIS_HASH
) {
607 addReply(c
, shared
.wrongtypeerr
);
611 addReplyMultiBulkLen(c
, c
->argc
-2);
612 for (i
= 2; i
< c
->argc
; i
++) {
613 addHashFieldToReply(c
, o
, c
->argv
[i
]);
617 void hdelCommand(redisClient
*c
) {
621 if ((o
= lookupKeyWriteOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
622 checkType(c
,o
,REDIS_HASH
)) return;
624 for (j
= 2; j
< c
->argc
; j
++) {
625 if (hashTypeDelete(o
,c
->argv
[j
])) {
627 if (hashTypeLength(o
) == 0) {
628 dbDelete(c
->db
,c
->argv
[1]);
634 signalModifiedKey(c
->db
,c
->argv
[1]);
635 server
.dirty
+= deleted
;
637 addReplyLongLong(c
,deleted
);
640 void hlenCommand(redisClient
*c
) {
642 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
643 checkType(c
,o
,REDIS_HASH
)) return;
645 addReplyLongLong(c
,hashTypeLength(o
));
648 static void addHashIteratorCursorToReply(redisClient
*c
, hashTypeIterator
*hi
, int what
) {
649 if (hi
->encoding
== REDIS_ENCODING_ZIPLIST
) {
650 unsigned char *vstr
= NULL
;
651 unsigned int vlen
= UINT_MAX
;
652 long long vll
= LLONG_MAX
;
654 hashTypeCurrentFromZiplist(hi
, what
, &vstr
, &vlen
, &vll
);
656 addReplyBulkCBuffer(c
, vstr
, vlen
);
658 addReplyBulkLongLong(c
, vll
);
661 } else if (hi
->encoding
== REDIS_ENCODING_HT
) {
664 hashTypeCurrentFromHashTable(hi
, what
, &value
);
665 addReplyBulk(c
, value
);
668 redisPanic("Unknown hash encoding");
672 void genericHgetallCommand(redisClient
*c
, int flags
) {
674 hashTypeIterator
*hi
;
676 int length
, count
= 0;
678 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.emptymultibulk
)) == NULL
679 || checkType(c
,o
,REDIS_HASH
)) return;
681 if (flags
& REDIS_HASH_KEY
) multiplier
++;
682 if (flags
& REDIS_HASH_VALUE
) multiplier
++;
684 length
= hashTypeLength(o
) * multiplier
;
685 addReplyMultiBulkLen(c
, length
);
687 hi
= hashTypeInitIterator(o
);
688 while (hashTypeNext(hi
) != REDIS_ERR
) {
689 if (flags
& REDIS_HASH_KEY
) {
690 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_KEY
);
693 if (flags
& REDIS_HASH_VALUE
) {
694 addHashIteratorCursorToReply(c
, hi
, REDIS_HASH_VALUE
);
699 hashTypeReleaseIterator(hi
);
700 redisAssert(count
== length
);
703 void hkeysCommand(redisClient
*c
) {
704 genericHgetallCommand(c
,REDIS_HASH_KEY
);
707 void hvalsCommand(redisClient
*c
) {
708 genericHgetallCommand(c
,REDIS_HASH_VALUE
);
711 void hgetallCommand(redisClient
*c
) {
712 genericHgetallCommand(c
,REDIS_HASH_KEY
|REDIS_HASH_VALUE
);
715 void hexistsCommand(redisClient
*c
) {
717 if ((o
= lookupKeyReadOrReply(c
,c
->argv
[1],shared
.czero
)) == NULL
||
718 checkType(c
,o
,REDIS_HASH
)) return;
720 addReply(c
, hashTypeExists(o
,c
->argv
[2]) ? shared
.cone
: shared
.czero
);